1

I have three commands that work, and I would like to make a loop that can do all the files, instead of having to type them for all files individually by name. I don't understand why it doesn't work as a loop.

What I want it to do, is put the word "Index" in the top left, and then on every line after that, the numbers from 1 through the number of lines in my .gcount files; Then paste the above and to the right of it, the contents of each of my .gcount files into new files with names "index_foo.gcount"

Based on the error messages (Directory nonexistent), the problem seems to be incorrect use of curly braces?

for files in ./*.gcount
do
    echo "Index" > index
    seq 1 1 $(cat $files | wc -l) >> index
    paste -d "\t" index $files > index_${files}
done
laur34
  • 125
  • 1
  • 6
  • That's because `index_$files` will expand to `index_./stuff.gcount` and since the directory `index_.` doesn't exist, Bash complains. Two possibilities to fix this: 1) use `for files in *.gcount`, so that you don't have any slashes in `files`. 2) only keep the basename from file with, just after the `do` keyword: `files=${files#./}`. This will get rid of the leading `./`. – gniourf_gniourf Mar 10 '15 at 23:12

1 Answers1

2

Update: The problem is that $files will include the directory name, which causes problems when you try to make an output filename from it. I originally thought it might be a spaces-in-filenames issue (only tested with in *), but gniourf_gniourf found the real problem:

That's because index_$files will expand to index_./stuff.gcount and since the directory index_. doesn't exist, Bash complains. Two possibilities to fix this: 1) use for files in *.gcount, so that you don't have any slashes in files. 2) only keep the basename from file with, just after the do keyword: files=${files#./}. This will get rid of the leading ./.

(Editing so that the post contains the real solution. rep ought to go to gniourf_gniourf, but we had a discussion where he was fine with an edit instead of posting his answer.)

Here's the original post, which might still demonstrate some good practice:

Works here, though there's a bug in that the "Index" line will be numbered too.

Check if any of your filenames contain spaces, as that will break it. To make scripts correctly handle such filenames, you should quote $files to make sure it expands into a single word, like

paste -d "\t" index "$files" > index_"${files}"

, and similarly for other lines. (The {} aren't needed there by the way, though they don't hurt.)

If you just want to add line numbers to your file, it's easier to use nl(1) (which seems to be POSIX, so it should be reasonably portable). Here's an example you could use which matches the format you're after:

{ echo Index; nl -nln -ba "$files"; } > index_"${files}"

The brackets let you redirect the output from many commands to the same place.

Another (slightly nitpicky) thing that's worth being aware of is that you can use -- with many commands to turn off flag processing, which lets you handle filenames that start with -. Here's an example: ls -- -weirdly_named_file.

You could fix the numbering bug in the original version by doing the following by the way:

seq 1 1 $(wc -l < "$files") > index
{ echo "Index"; paste -d "\t" index "$files"; } > index_"${files}"

This question contains other variants for adding a line to the beginning of a file.

Community
  • 1
  • 1
Ulfalizer
  • 4,274
  • 1
  • 18
  • 27
  • `{ echo "Index"; cat index_"${files}"; } > index_"${files}"` doesn't work: the redirection `> index_"$files"` will first truncate the file and then run the group and redirect its output to `index_"$files"`… so you'll only get `Index` in that file. What you want is `echo "Index"` just before the `paste` in a group. – gniourf_gniourf Mar 11 '15 at 08:25
  • @gniourf_gniourf: Thanks! That's embarrassing. Another issue was that `wc -l "$files"` includes the filename in its output. – Ulfalizer Mar 11 '15 at 08:44