27

I need to process the shared library dependencies of a library from a bash script. The for command processes word-by-word:

for DEPENDENCY in `otool -L MyApplication | sed 1d`
do
    ...
done

What is the way to process the results line-by-line?

Jens
  • 65,924
  • 14
  • 115
  • 171
Dave Mateer
  • 17,170
  • 15
  • 93
  • 147

7 Answers7

38

You should use the read command.

otool -L MyApplication | sed 1d | \
while read i
do
  echo "line: " $i
done

See bashref for a description of the read builtin, and its options. You can also have a look at the following tutorial for examples of using read in conjunction with for.

tonio
  • 10,079
  • 2
  • 44
  • 59
  • 11
    Warning: if you need to set variables in the while loop (i.e. store what you found in otool's output), they'll vanish as soon as the loop exits because it's part of a pipeline and therefore runs in a subshell. In bash, you can get around this with a bit of rearranging: `while read i; do ... done < – Gordon Davisson Jun 24 '10 at 22:37
  • 2
    Another warning: This will delete a lot of spaces and backslashes. Use `read -r i` to read lines without such modifications. – Jens May 24 '12 at 21:04
  • @Jens, no, it's the echo command eating spaces. `echo "line: $i"` will prevent that. – James Morris Jul 14 '13 at 22:27
  • @JamesMorris No, it's the read that eats leading and trailing whitespace and backslashes. Try `printf ' foo\\bar \n'|read a; echo ""` and then with `read -r`. – Jens Jul 16 '13 at 19:03
  • @Jens, read the man page, nowhere does it mention whitespace. the `-r` option simply prevents special treatment of backslashes. – James Morris Jul 17 '13 at 01:12
  • Why is it that I have exclude the first line with this answer? (sed 1d). I'm noticing if I exclude '1d' it spits back the sed help page (command fails to run). – jersey bean Aug 23 '17 at 01:11
  • @jersey-bean `otool` output consists in a first line with the name of the library followed by `:`. Then you have the dependencies, one per line.So we strip the first line since the original question was about iterating over dependencies. – tonio Aug 23 '17 at 07:50
  • 1
    @Jens, ...it's the act of clearing `IFS` in the best-practice invocation of `IFS= read -r i` that eliminates leading and trailing whitespace pruning. – Charles Duffy Oct 06 '17 at 20:01
  • @JamesMorris, jens *was* actually correct that leading and trailing whitespace gets pruned, just incorrect about what to do about it. This is covered in more detail in [BashFAQ #1](http://mywiki.wooledge.org/BashFAQ/001). – Charles Duffy Oct 06 '17 at 20:02
  • And in bash, the test demonstrating all these differences needs to be more like `printf ' foo\\bar \n' | { IFS= read -r a; echo ""; }`; without explicit grouping, the `echo` doesn't run in the same subshell that was spawned as part of the pipeline ([BashFAQ #24](http://mywiki.wooledge.org/BashFAQ/024)). – Charles Duffy Oct 06 '17 at 20:04
9
otool -L MyApplication | sed 1d | while read line ;
do
  # do stuff with $line
done
Jim Lewis
  • 41,827
  • 6
  • 83
  • 95
3

Try changing the Internal Field Separator to a newline. By default, bash is going to separate tokens by spaces when using a for loop. Your IFS setting will make the for loop split up the tokens based on whatever string IFS is equal to (thus splitting up the list tokens by newlines instead of by tokens by spaces).

[bash ] $ IFS="
"
[bash ] $ for DEPENDENCY in `otool -L MyApplication | sed 1d`
do
    ....
done
aoeu
  • 1,098
  • 2
  • 13
  • 22
2

You have to use shell builtin read, but be careful with lines containing spaces and tabs. I suggest locally change value of $IFS:

 otool -L MyApplication | sed 1d | \
 while IFS= read i; do
     echo "line: " $i
 done
Jérôme Pouiller
  • 7,845
  • 4
  • 38
  • 43
1

The modern bash way:

while read i; do
    ...
done < <(otool -L MyApplication | sed 1d)

Note: the two "<"s above is not a typo, that's the correct syntax.

Ron HD
  • 123
  • 7
0

You can use awk to process things on a per-line basis. Exactly what's the best way depends on what you're trying to do though.

Daniel Egeberg
  • 8,289
  • 30
  • 44
0

you can do it awk as well. No need to use a bash for loop

otool -L MyApplication | awk 'NR>1
{
  # do your stuff here since awk process line by line.
}' 
ghostdog74
  • 307,646
  • 55
  • 250
  • 337