36

我想遍历从echo $VARIABLE命令中获得的路径列表。

例如:

echo $MANPATH将返回

/usr/lib:/usr/sfw/lib:/usr/info

所以这是三个不同的路径,每个路径用冒号分隔。我想遍历这些路径中的每一个。有没有办法做到这一点?谢谢。

感谢到目前为止的所有回复,看来我实际上根本不需要循环。我只需要一种取出冒号的方法,这样我就可以ls在这三个路径上运行一个命令。

4

10 回答 10

62

您可以设置内部字段分隔符:

( IFS=:
  for p in $MANPATH; do
      echo "$p"
  done
)

我使用了一个子外壳,因此 IFS 的更改不会反映在我当前的外壳中。

于 2012-07-25T18:05:46.053 回答
18

在 Bash 中,执行此操作的规范方法是read适当地使用内置函数:

IFS=: read -r -d '' -a path_array < <(printf '%s:\0' "$MANPATH")

这是唯一强大的解决方案:将完全按照您的意愿进行操作:在分隔符上拆分字符串,:并且对于空格、换行符和全局字符(如*[ ]等)是安全的(与其他答案不同:它们都已损坏)。

在此命令之后,您将拥有一个数组path_array,您可以在其上循环:

for p in "${path_array[@]}"; do
    printf '%s\n' "$p"
done
于 2015-04-29T16:43:39.873 回答
17

您可以使用 Bash 的模式替换参数扩展来填充循环变量。例如:

MANPATH=/usr/lib:/usr/sfw/lib:/usr/info

# Replace colons with spaces to create list.
for path in ${MANPATH//:/ }; do
    echo "$path"
done

注意:不要将替换扩展括在引号中。您希望 MANPATH 中的扩展值被 for 循环解释为单独的单词,而不是单个字符串。

于 2012-07-25T18:04:37.863 回答
5

通过这种方式,您可以安全地通过$PATH单个循环,而循环$IFS内部或外部将保持不变。

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

您可以检查 的值$IFS

IFS='xxxxxxxx'
while IFS=: read -d: -r path; do
    echo "${IFS}${path}"
done <<< "${PATH:+"${PATH}:"}"

输出将是这样的。

xxxxxxxx/usr/local/bin
xxxxxxxx/usr/bin
xxxxxxxx/bin

参考StackExchange 上的另一个问题

于 2015-06-24T04:00:37.563 回答
1
for p in $(echo $MANPATH | tr ":" " ") ;do
    echo $p
done
于 2012-07-25T18:09:34.677 回答
1

这也可以在命令行上用 Python 解决:

python -c "import os,sys;[os.system(' '.join(sys.argv[1:]).format(p)) for p in os.getenv('PATH').split(':')]" echo {}

或作为别名:

alias foreachpath="python -c \"import os,sys;[os.system(' '.join(sys.argv[1:]).format(p)) for p in os.getenv('PATH').split(':')]\""

使用示例:

foreachpath echo {}

这种方法的优点是{}会被每条路径连续替换。这可用于构造各种命令,例如列出$PATH. 包括名称中带有空格的目录:

foreachpath 'for e in "{}"/*; do du -h "$e"; done'

这是一个通过为in$PATH中的每个文件和目录创建符号链接来缩短变量长度的示例。这对日常使用没有用,但如果您在容器中收到错误消息,可能会很有用,因为使用完整作为命令行的一部分......$PATH$HOME/.allbintoo many argumentsdockerbitbake$PATH

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"

理论上,这也应该加快常规 shell 的使用和 shell 脚本,因为搜索每个执行的命令的路径更少。不过,这hacky,所以我不建议任何人缩短$PATH这种方式。

不过,foreachpath别名可能会派上用场。

于 2018-03-08T10:43:02.093 回答
1
IFS=:
arr=(${MANPATH})
for path in "${arr[@]}" ; do # <- quotes required
   echo $path
done

...它确实会处理空格 :o) 但如果您有以下内容,还会添加空元素:

:/usr/bin::/usr/lib:

...然后索引 0,2 将为空(''),不能说为什么索引 4 根本没有设置

于 2015-09-11T02:19:57.607 回答
0

您可以使用 ${} 表示法中的 Bash for X 来完成此操作:

for p in ${PATH//:/$'\n'} ; do
    echo $p;
done
于 2012-07-25T18:04:57.693 回答
0

结合以下想法:

代码:

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

输出:

1   foo
2   bar baz
3   spam
4   eggs
5   
于 2019-03-04T03:53:44.910 回答
0

OP 的更新想要ls生成的文件夹,并指出ls只需要一个空格分隔的列表。

ls $(echo $PATH | tr ':' ' ')很好很简单,应该很好地满足要求。

于 2022-01-08T20:08:37.813 回答