Bash while 循环使用 IFS 从冒号分隔的路径列表中读取

Bash while 循环使用 IFS 从冒号分隔的路径列表中读取

我正在尝试编写一个 bash 函数,其行为where类似于tcsh.在 中tcshwhere列出了具有给定名称的所有内置函数、别名和可执行文件的绝对路径PATH,即使它们被隐藏,例如

tcsh> where tcsh
/usr/bin/tcsh
/bin/tcsh

作为其中的一部分,我想循环遍历其中的所有内容$PATH,并查看是否存在具有适当名称的可执行文件。

以下 bash 片段旨在循环遍历以冒号分隔的路径列表,并打印每个组件,后跟换行符,但是,它似乎只是将所有内容的全部内容打印$PATH在一行上

#!/bin/bash
while IFS=':' read -r line; do
    printf "%s\n" "$line"
done <<< "$PATH"

按照现在的情况,bash where只需./where打印/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

那么,如何设置 while 循环,以便循环变量的值依次是冒号分隔的路径列表的每个段?

答案1

read用于分隔它读取的行中的单词,直到其中的任何字符第一次出现时才IFS告诉读取。read

IFS=: read -r a b

将读取一行,将第一部分之前的部分放入:$a并将其余部分放入$b

IFS=: read -r a

会将整行(其余部分)放入$a(除非该行仅包含一个字符:并且它是该行的最后一个字符)。

如果你想读到第一个:,你可以使用read -d:ksh93zshbash仅)。

printf %s "$PATH" | while IFS= read -rd: dir || [ -n "$dir" ]; do
  ...
done

(我们没有使用<<<as 来添加额外的换行符)。

或者您可以使用标准分词:

IFS=:; set -o noglob
for dir in $PATH""; do
  ...
done

现在请注意一些警告:

  • $PATH组件表示当前目录。
  • $PATH表示当前目录(即$PATH包含一个当前目录的组件,因此while read -d:在这种情况下循环将是错误的)。
  • //file/file与某些系统上的情况不同,因此如果$PATH包含/,则需要小心诸如 之类的事情$dir/$file
  • 一个未设置$PATH 表示默认要使用搜索路径,它与设置但空的 $PATH 不同。

现在,如果它仅相当于tcsh/zshwhere命令,您可以使用bash's type -a

更多阅读:

答案2

不要使用 shell 循环来处理文本。

相反,请使用awk、 或tr、 甚至sed

printf %s\\n "$PATH" | tr ':' '\n'

printf %s "$PATH" | awk 'BEGIN {RS=":"}; 1'

或者,由于这是您正在处理的 shell 变量,因此只需使用bash模式替换:

echo "${PATH//:/
}"

(看LESS=+/parameter/pattern man bash。)

答案3

纯粹bash,它与通配符的第二种方法,但这是一行:

echo -e "${PATH//:/"\n"}"

答案4

外壳实用程序:

echo $PATH | tr ':' '\n'

相关内容