2

I have a directory full of files. My goal is to append text to the beginning of the file. The text that goes at the beginning is the same for each file. This is my attempt

#!/bin/bash                                                                                                                                                    

for file in `find . -type f -executable`;do                                                                                                                                 
    sed '/\#!/bin/bash\/a Hello Word'                                                                                                                                            


done

My script does nothing but crash

Rui F Ribeiro
  • 56,709
  • 26
  • 150
  • 232
K.U
  • 25

3 Answers3

5
#!/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 &quot;Hello World&quot;" . x | ex "$f" done ' find-sh {} +

Wildcard
  • 36,499
0

what you are trying to achieve ? to find out the executable scripts, use the below command

find . -type f -perm /u+x,g+x,o+x | while read file
do
    filename=$(echo ${file} | awk -F/ '{print $NF}')
    echo "File Name : ${filename}"
done

If you are trying append Hello World after the shebang line, then your sed command should be

 sed '/\#!/bin/bash/a Hello Word' ${file}

if you want to make the changes in the file itself, then add -i in sed command

Kamaraj
  • 4,365
  • Hi, I'm trying to append Hello World to executable files in a directory. I know how to find executable files, but I don't quite know how I could append text to those file – K.U Nov 10 '16 at 23:33
  • When I run your code to find executables file I got: File Name : ./insert.sh Is there a way I can only get: File Name : insert.sh – K.U Nov 10 '16 at 23:39
  • modified the answer – Kamaraj Nov 11 '16 at 03:35
-2

find . -type f -executable -exec sed '/^#!\/bin\/bash/a Hello World' {} \;

ewatt
  • 463