#!/bin/bash
for file in find . -type f -executable
;do
sed '/#!/bin/bash/a Hello Word'
fi
done
This script has a great number of problems.
First, it's not a valid Bash script, because you have fi
with no corresponding if
.
Stylistically (after removing the fi
line), I would remove the empty line before done
and add a space before do
. But that's relatively trivial.
Now, as to best practices, you are using backticks for command substitution rather than the recommended modern form, $(...)
. Backticks are supported purely for historical reasons and are not recommended for any new scripts; see:
You have a robustness issue in that you are looping over the output of find
—a very bad idea, and totally unnecessary. Your script will break on any filenames containing whitespace or special characters. See:
If you want your script to be portable, you should stick to POSIX specified features whenever possible. In particular the -executable
primary to find
is not specified by POSIX. Consider using -perm +700
instead.
Also in the realm of portability, using the "append" command to Sed (a
) without a following \<newline>
sequence works in GNU Sed, but is not standard.
You set the file
variable in your for
loop to the name of each file in turn (assuming no special characters or whitespace in the filenames, which will cause the file
variable to contain something which is not a filename), but you never actually use the file
variable.
Your Sed command is not given any file to run on, so it will attempt to run on standard input. Thus when you run the script it will simply wait for input.
Your Sed script itself is incorrect, independent of the fact that it's missing a filename to operate on.
If you use /
as a delimiter for a regex (which is most usual), you need to backslash-escape all instances of /
which occur within the regex. The only portion of your command which will be read as a regex is /\#!/
, and the rest (starting with bin
) will be interpreted as a Sed command.
Instead, the usual solution would be to replace each /
other than the final regex delimiter with \/
. (I see that you escaped only the last slash, which should not be escaped.)
There is a little-known feature in Sed, which you could use to your advantage here. Any character (other than a backslash or newline) can be used as a regex delimiter, rather than only using a slash. So rather than using /#!\/bin\/bash/
as a Sed address, you could use the equivalent \:#!/bin/bash:
Now if you've handled all of the above points, you will have a working script. It may not do what you want it to do, but it will actually do something. Such a script would look like this:
#!/bin/bash
find . -type f -perm -700 -exec sed ':#!/bin/bash:a
Hello World' {} +
What does this script do? It searches the current directory recursively for all files with the executable bit set (for the owner), and for each such file, prints the entire file with the text Hello World
appended after any lines which contain the text #!/bin/bash
.
Sed is not actually designed for editing files in place; it is the Stream EDitor. GNU Sed will allow you to edit files in place using the -i
switch, but I would just use the standard tool ex
for file editing.
But there is another point here. If you want to add the line Hello World
in a Bash script, it won't actually do anything, as Hello
is not a valid command name. Perhaps what you want is to print the text "Hello World" in the Bash script, in other words to add echo "Hello World
, which could make sense.
Now we're into the realm of clarifying more exactly what your script is supposed to do.
The Final Script
So my more exact statement of the specifications for this script are:
- The script shall find all regular files in the current directory (or any subdirectory recursively) which have the executable bit set for the owner.
- For each such file, the script shall check whether the first line of the file exactly equals the string
#!/bin/bash
.
- Only for files with this exact first line, the script shall insert the exact text
echo "Hello World"
, followed by a newline character, after the first line of the file. (This change shall be saved to the file, not printed to standard out.)
Here is a script matching those exact specifications, using only POSIX tools and features:
#!/bin/sh
find . -type f -perm -700 -exec sh -c '
for f
do
head -n 1 "$f" | grep -qFx "#!/bin/bash" &&
printf "%s\n" "1a" "echo "Hello World"" . x | ex "$f"
done
' find-sh {} +