Reputation: 755
I want to loop through a path list that I have gotten from an echo $VARIABLE
command.
For example:
echo $MANPATH
will return
/usr/lib:/usr/sfw/lib:/usr/info
So that is three different paths, each separated by a colon. I want to loop though each of those paths. Is there a way to do that? Thanks.
Thanks for all the replies so far, it looks like I actually don't need a loop after all. I just need a way to take out the colon so I can run one ls
command on those three paths.
Upvotes: 43
Views: 25817
Reputation: 1322
OP's update wants to ls
the resulting folders, and has pointed out that ls
only requires a space-separated list.
ls $(echo $PATH | tr ':' ' ')
is nice and simple and should fit the bill nicely.
Upvotes: 0
Reputation: 3328
Combining ideas from:
code:
PATHVAR='foo:bar baz:spam:eggs:' # demo path with space and empty
printf '%s:\0' "$PATHVAR" | while IFS=: read -d: -r p; do
echo $p
done | cat -n
output:
1 foo
2 bar baz
3 spam
4 eggs
5
Upvotes: 1
Reputation: 10376
This can also be solved with Python, on the command line:
python -c "import os,sys;[os.system(' '.join(sys.argv[1:]).format(p)) for p in os.getenv('PATH').split(':')]" echo {}
Or as an alias:
alias foreachpath="python -c \"import os,sys;[os.system(' '.join(sys.argv[1:]).format(p)) for p in os.getenv('PATH').split(':')]\""
With example usage:
foreachpath echo {}
The advantage to this approach is that {}
will be replaced by each path in succession. This can be used to construct all sorts of commands, for instance to list the size of all files and directories in the directories in $PATH
. including directories with spaces in the name:
foreachpath 'for e in "{}"/*; do du -h "$e"; done'
Here is an example that shortens the length of the $PATH
variable by creating symlinks to every file and directory in the $PATH
in $HOME/.allbin
. This is not useful for everyday usage, but may be useful if you get the too many arguments
error message in a docker
container, because bitbake
uses the full $PATH
as part of the command line...
mkdir -p "$HOME/.allbin"
python -c "import os,sys;[os.system(' '.join(sys.argv[1:]).format(p)) for p in os.getenv('PATH').split(':')]" 'for e in "{}"/*; do ln -sf "$e" "$HOME/.allbin/$(basename $e)"; done'
export PATH="$HOME/.allbin"
This should also, in theory, speed up regular shell usage and shell scripts, since there are fewer paths to search for every command that is executed. It is pretty hacky, though, so I don't recommend that anyone shorten their $PATH
this way.
The foreachpath
alias might come in handy, though.
Upvotes: 1
Reputation: 33
IFS=:
arr=(${MANPATH})
for path in "${arr[@]}" ; do # <- quotes required
echo $path
done
... it does take care of spaces :o) but also adds empty elements if you have something like:
:/usr/bin::/usr/lib:
... then index 0,2 will be empty (''), cannot say why index 4 isnt set at all
Upvotes: 1
Reputation: 309
In this way you can safely go through the $PATH
with a single loop, while $IFS
will remain the same inside or outside the loop.
while IFS=: read -d: -r path; do # `$IFS` is only set for the `read` command
echo $path
done <<< "${PATH:+"${PATH}:"}" # append an extra ':' if `$PATH` is set
You can check the value of $IFS
,
IFS='xxxxxxxx'
while IFS=: read -d: -r path; do
echo "${IFS}${path}"
done <<< "${PATH:+"${PATH}:"}"
and the output will be something like this.
xxxxxxxx/usr/local/bin
xxxxxxxx/usr/bin
xxxxxxxx/bin
Reference to another question on StackExchange.
Upvotes: 8
Reputation: 46823
The canonical way to do this, in Bash, is to use the read
builtin appropriately:
IFS=: read -r -d '' -a path_array < <(printf '%s:\0' "$MANPATH")
This is the only robust solution: will do exactly what you want: split the string on the delimiter :
and be safe with respect to spaces, newlines, and glob characters like *
, [ ]
, etc. (unlike the other answers: they are all broken).
After this command, you'll have an array path_array
, and you can loop on it:
for p in "${path_array[@]}"; do
printf '%s\n' "$p"
done
Upvotes: 28
Reputation: 241828
You can set the Internal Field Separator:
( IFS=:
for p in $MANPATH; do
echo "$p"
done
)
I used a subshell so the change in IFS is not reflected in my current shell.
Upvotes: 67
Reputation: 84343
You can use Bash's pattern substitution parameter expansion to populate your loop variable. For example:
MANPATH=/usr/lib:/usr/sfw/lib:/usr/info
# Replace colons with spaces to create list.
for path in ${MANPATH//:/ }; do
echo "$path"
done
Note: Don't enclose the substitution expansion in quotes. You want the expanded values from MANPATH to be interpreted by the for-loop as separate words, rather than as a single string.
Upvotes: 18
Reputation: 61512
You can use Bash's for X in ${} notation to accomplish this:
for p in ${PATH//:/$'\n'} ; do
echo $p;
done
Upvotes: 0