Bash 为什么用'read-r-a line'拆分我的$PATH有效,而用'while read-r line'不行?

Bash 为什么用'read-r-a line'拆分我的$PATH有效,而用'while read-r line'不行?,bash,Bash,只是注意到一些奇怪的事情,我无法解释: 当我使用read-a拆分$PATH变量时,一切正常 IFS=: read -r -a lines <<< "$PATH" for line in "${lines[@]}"; do echo "$line"; done 您的循环处理一行时,它不是循环。因此,完整的路径存储在字段行 当您指定更多字段时,路径将被分割到这些字段(最后一个字段将获得剩余字段): 而IFS=:read-

只是注意到一些奇怪的事情,我无法解释:

当我使用
read-a
拆分
$PATH
变量时,一切正常

IFS=: read -r -a lines <<< "$PATH"
for line in "${lines[@]}"; do echo "$line"; done

您的
循环处理一行时,它不是循环。因此,完整的路径存储在字段

当您指定更多字段时,路径将被分割到这些字段(最后一个字段将获得剩余字段):

而IFS=:read-r line field2 field3 otherfields;做回显“$line”;完成你可以完成这项工作;从使用
IFS=:
切换到使用
-d:
,并在输入流的末尾附加一个

while IFS= read -r -d: line; do echo "$line"; done <<< "$PATH:"
而IFS=read-r-d:line;做回显“$line”;完成
为什么用
read-r-a行
拆分我的$PATH有效,而用
拆分read-r行
无效

因为
read-r行
读取整行,然后在读取单个整行之后,如果
IFS
,则该行被吐在
上。因为您只提供了一个变量来读取
,所以所有的行都在该变量中。您可以在第一个元素和其余元素上拆分线:

IFS=: read -r part1 rest_of_parts <<<"$line"
您可以使用bash扩展名
read
-d
,使
read
不读取整行,但直到一个字符(但我需要忽略
read
退出状态,不知道为什么):

它很好用

拆分为一个数组将提供一个开放的元素数,您所期望的也是如此

拆分为一个变量也可以做同样的事情,但是当它没有提供用于存放数据的变量名时,它会停止拆分,并将剩余的放入最后一个变量

试试这个:

$: IFS=: read -r a b c <<< "$PATH"
$: printf "[%s]\n" "$a" "$b" "$c"

$:IFS=:read-r a b c不是您问题的答案,但是
mapfile
命令可能更适合这里。
IFS
将变量拆分为单词,而不是行。但是
PATH
是单行。为什么不必要地用换行来中断对目录名的支持?(:面:)
while IFS= read -r -d: line; do echo "$line"; done <<< "$PATH:"
IFS=: read -r part1 rest_of_parts <<<"$line"
$ export PATH=/usr/bin       # reset PATH to something short
$ cd /tmp/
$ mkdir temp$'\n'dir         # create a directory with a newline in the name
$ ls -d tem*
'temp'$'\n''dir'/
$ cd temp$'\n'dir
$ printf "%s\n" '#!/bin/bash' 'echo hello world' > script.sh
$ chmod +x ./script.sh       # add a script in that directory
$ export PATH="$PATH:$PWD"   # add that directory to path
$ ./script.sh                # yes. yes, it works
hello world
$ IFS=: read -r -a lines <<< "$PATH"
$ declare -p lines
declare -a lines=([0]="/usr/bin" [1]="/tmp/temp")
#                                              ^^^^ newline and 'dir' is missing
# That is because `read` reads _one line_ and one line only
# _after_ reading that one line that _one line_ is split on IFS
# so any more lines are ignored.
$ while IFS= read -r -d':' line || [[ -n "$line" ]]; do declare -p line; done < <(printf "%s" "$PATH")
declare -- line="/usr/bin"
declare -- line="/tmp/temp
dir"
$ mapfile -d: -t lines < <(printf "%s" "$PATH")
$ declare -p lines
declare -a lines=([0]="/usr/bin" [1]=$'/tmp/temp\ndir')
$: IFS=: read -r a b c <<< "$PATH"
$: printf "[%s]\n" "$a" "$b" "$c"