Bash while loop read from colon-delimited list of paths using IFS
read
uses IFS
to separate the words in the line it reads, it doesn't tell read
to read until the first occurrence of any of the characters in it.
IFS=: read -r a b
Would read one line, put the part before the first :
in $a
, and the rest in $b
.
IFS=: read -r a
would put the whole line (the rest) in $a
(except if that line contains only one :
and it's the last character on the line).
If you wanted to read until the first :
, you'd use read -d:
instead (ksh93
, zsh
or bash
only).
printf %s "$PATH" | while IFS= read -rd: dir || [ -n "$dir" ]; do
...
done
(we're not using <<<
as that adds an extra newline character).
Or you could use standard word splitting:
IFS=:; set -o noglob
for dir in $PATH""; do
...
done
Now beware of few caveats:
- An empty
$PATH
component means the current directory. - An empty
$PATH
means the current directory (that is,$PATH
contains one component which is the current directory, so thewhile read -d:
loop would be wrong in that case). //file
is not necessary the same as/file
on some system, so if$PATH
contains/
, you need to be careful with things like$dir/$file
.- An unset $PATH means a default search path is to be used, it's not the same as a set but empty $PATH.
Now, if it's only the equivalent of tcsh
/zsh
's where
command, you could use bash
's type -a
.
More reading:
- What's a safe and portable way to split a string in shell programming?
- Understanding "IFS= read -r line"
- Why not use "which"? What to use then?
Don't use shell loops to process text.
Instead, use awk
, or tr
, or even sed
.
printf %s\\n "$PATH" | tr ':' '\n'
printf %s "$PATH" | awk 'BEGIN {RS=":"}; 1'
Or, since this is a shell variable you are processing, just use bash
pattern substitution:
echo "${PATH//:/
}"
(See LESS=+/parameter/pattern man bash
.)