Hacker News new | past | comments | ask | show | jobs | submit login

The following does not work on files with spaces according to the article:

  for i in $(ls *.mp3); do
    some command $i
  done
So does that mean, that "for" will do something per word of the output of $, rather than per line of output of it?

What to do if I want to do something for every line? What for example if I really want the output of ls, find (or any other command you can put int he $()) and loop through that line per line, even if some output has spaces?

Thanks.




There are a couple of ways you can do it. Don't do this with `ls` though; there are better ways to get that info in scripts.

    # Loops over lines, in a subshell
    prints_lines | while read -r line; do
      some_command "$line"
    done

    # Loops over lines, in the current shell
    while read -r line; do
      some_command "$line"
    done < <(prints_lines)

    # If for some reason you really want a for loop
    oldIFS=$IFS
    IFS=$'\n' lines=($(prints_lines))
    IFS=$oldIFS
    for line in "${lines[@]}"; do
      some_command "$line"
    done


> So does that mean, that "for" will do something per word of the output of $, rather than per line of output of it?

Correct. The argument to "for" is a list of words.

> What to do if I want to do something for every line?

Use a while loop.

    find /some/dir/ -type f |
    while read -r line; do
       ; # something with $line
    done
PS. You should almost always use `find` instead of `ls` in shell scripts. Given a pattern, `ls` will exit non-zero if nothing matches it, and you should be treating non-zero exits like you would exceptions in other languages.


One thing to be careful of when doing "while read..." is that a new shell is started on each iteration, so you cannot for example set a variable within the loop that you can use later in the script, as its value will be lost when the shell process exits.


> a new shell is started on each iteration

This is not actually true.

    printf "\n\n\n" | while read i; do a="x$a"; echo "$a"; done
    x
    xx
    xxx
The accumulator value even carries over after the while loop:

    printf "\n\n\n" | ( while read i; do a="x$a"; echo "$a"; done ; echo "$a" )
    x
    xx
    xxx
    xxx
(Technically, whether or not the loop body is executed in a subshell may be implementation dependent. Haven't looked at the POSIX shell spec in a while, but I seem to remember an old ksh that actually used subshells. At any rate, none of the modern sh's and bash force a subshell.)

What is true, however, is that a pipeline will execute in a subshell. Maybe that's what you're getting at here, and it is an important caveat.

    a=y; printf "\n\n\n" | while read i; do a="x$a"; echo "$a"; done; echo "$a"
    xy
    xxy
    xxxy
    y


Ah, ok. True, when I have had this issue it was after doing something like 'grep "pattern" file | while read ...'. I did not realize it was the pipe that caused this.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: