2

I have a bunch of javascript files and I need to add a comment on a new line at the beginning of every file that contains the filename e.g.

/* app.js */

I'd like to do this recursively on a folder from the terminal on my mac.

Can anyone help?

Thanks!

Alistair Colling
  • 1,363
  • 2
  • 19
  • 29

3 Answers3

1

First, we have to locate those files:

$ find $folder_name -type f -name "*.js" -print

This will locate all of the files with the suffix of *.js. The type -f means only look for files (just incase you have a directory name that ends in .js). I will assume that your JavaScript file names don't contain whitespace, or control characters like <NL> characters in the name. Otherwise, we'll have to do a bit more work.

We can use that find statement to locate all of those files, then we can munge those files. Once we find those files, we have to figure out how to munge the file to get the comment line on top.

We'll take three step approach:

  1. First, we'll create a new file with the comment line in it.
  2. Next we'll concatenate the old file on the end of the new file.
  3. We'll rename the new file to the old file.

For the first step, we'll use echo to create a new file with the old name. Then, we'll use the cat command to concatenate the old file on the new file. And, finally we'll use mv to rename the newly constructed file to the old name.

find $javascript_dir -type f -name "*.js" -print | while read file_name
do
    basename=$(basename $file_name)
    echo "/* $basename */" > "$basename.tmp"
    cat "$file_name" >> "$basename.tmp"
    mv "$basename.tmp" "$file_name"
done

Note that > redirects the output of the command to the named file, and that >> appends the output of the command to the named file. mv stands for move. In Unix, file are actually stored by inode identify. There's a table of contents that matches the inode to the full name of the file (including the directory). Thus, moving a file in Unix is the same as simply renaming it, so the directory name is changed. In Windows batch, there are separate move and rename commands. In Unix, they're both mv.


Addition

We could use sed to do an in place prepending of the line:

find . -name "*.js" -print | while read file_name
do
    basename=$(basename $file_name)
    sed -i "" "1i\\
    /* $basename */
    " "$file_name"
done

sed is a powerful line editor, and most people use only its substitution command mode. However, sed scripts can be way more complex and rich (and obscure which is why you rarely see them).

David W.
  • 105,218
  • 39
  • 216
  • 337
0

You can use cat - and a temp file:

# adds filename to top of each file in $DIR
DIR=. # set to whatever directory
FILES=$(find $DIR -type f)
for FILE in $FILES; do 
  FILE=$(echo $FILE | sed -e s/\.\\\///) # remove leading "./" if DIR is "."
  echo "/* $FILE */" | cat - $FILE > tempfile.tmp # add the line using a temp file
  mv tempfile.tmp $FILE # move the temp file back to the original file
done

Note that this will add the full path of the file relative to DIR if DIR is not '.'. To add only the filename itself you could use:

  echo "/* $(basename $FILE) */" | cat - $FILE > tempfile.tmp # add the line using a temp file
Bill Doughty
  • 2,250
  • 14
  • 20
  • If you're going to use the `find` command, why find the files, then use a `for` loop to loop through them? Just pipe a `while read` loop to the end of the of the `find` command. I do like piping the `cat` to the end of the `echo` command. – David W. Aug 19 '16 at 16:12
  • Sure, you could just pipe the `find` output to `while read`. I just added the intermediate FILES variable for clarity and readability. FILES could be any list of files generated by `ls`, `find`, or any other function or script. – Bill Doughty Aug 19 '16 at 16:18
-1

You can use sed for this, try something like this:

#!/bin/bash

for i in $(find ./ -name *.js);
do
 `sed -i 's/$i \*\//$i \*\/\n&/' "$i"`
done

Insert a newline with sed could be a problem since not all version works the same. To have an overview see this reference. You can for example replace the substitution part with G or {G;}. It depends on your sed version. Similar Questions are already answered here and here

Another interesting solution:

If you want to append a newline after the first line you can also use this:

sed -i "1a\ " test.js

So change the script above into this:

#!/bin/bash

for i in $(find ./ -name *.js);
do
 `sed -i "1a\ " $1`
done
Community
  • 1
  • 1
Zelldon
  • 5,396
  • 3
  • 34
  • 46
  • Thanks. I can't seem to get this to run from inside a shell script. Am I doing something wrong? Or is there a command I can run from the command line? – Alistair Colling Aug 19 '16 at 15:32
  • Have you tested this script? Your loop is going to increment over the words `find`, `./`, `-name`, and then all files in the current directory that end in `*.js`. You also have capitalized `For`, `Do`, `Sed`, and `Done`. Finally, why use _substitution_ in a `sed` script if you really want to _insert_ a line? – David W. Aug 19 '16 at 16:01
  • Sorry i was underway so i hadn't tested and on android app the word starts on each new line with capital letter. – Zelldon Aug 19 '16 at 16:11
  • If you used `1i\` rather than `1a\` in your script, it would prepend the line. You also shouldn't use *back ticks* around the `sed` command. Plus, the inserted/appended line goes on the _next_ line. And, for Mac's version of `sed`, you need a null quote after the `-i`. Note when you put the `sed` in a shell script, you have to double up the backslash. See my answer. – David W. Aug 19 '16 at 17:41
  • @DavidW. thanks for the suggestions but I want to use 1a since the problems i told before. Why i shouldn't use `? It works for me. Also the double backslash is not necessary tested on arch linux. – Zelldon Aug 19 '16 at 20:03