关于Bash中read、IFS与循环的异常行为的技术疑问
Bash中read、IFS与循环的异常行为技术解析
首先先把你的测试脚本和对应的输出贴出来,方便大家对照理解:
测试脚本
names=Tom,Jack,Bill echo 'For loop output:' IFS=',' read -ra names_array <<< "$names" for name in "${names_array[@]}"; do echo "$name" done echo 'While loop output (correct):' while read -rd, name; do echo "$name" done <<< "$names," echo 'While loop output (missing last element):' while read -rd, name; do echo "$name" done <<< "$names" echo 'While loop output (not using IFS at all):' while IFS=',' read -r name; do echo "$name" done <<< "$names,"
脚本输出
For loop output:
Tom
Jack
BillWhile loop output (correct):
Tom
Jack
BillWhile loop output (missing last element):
Tom
JackWhile loop output (not using IFS at all):
Tom,Jack,Bill,
接下来咱们逐个解答你的疑问:
疑问1:为什么数组读取不需要末尾逗号,而-d选项的while循环需要?
这俩场景的核心是read命令的两种完全不同的工作逻辑:
- 当用
IFS=',' read -ra读取到数组时,read的行为是按IFS指定的分隔符把整个输入拆分成若干字段,然后把所有拆分出来的字段依次存入数组。哪怕输入的最后没有分隔符,只要最后一段有内容(比如这里的Bill),它也会被当成一个独立字段加入数组。所以"Tom,Jack,Bill"拆分后刚好是3个字段,数组自然就有3个元素。 - 而用
read -rd,时,-d是指定单次读取的终止符——每次read会从输入里读取内容,直到遇到你指定的','就停下来,把终止符之前的内容赋值给变量,同时丢弃这个终止符。如果输入的最后没有这个终止符(比如"Tom,Jack,Bill"),那么最后一段Bill后面没有',',当read读到输入末尾(EOF)时,它会返回失败状态,循环直接终止,自然就漏掉了最后一个元素。而给输入加个末尾逗号后,"Tom,Jack,Bill,"的最后有个',',read会读取到Bill后遇到终止符,完成一次赋值,下一次read遇到EOF才退出,这样三个元素就都能读到了。
疑问2:最后一个while循环里,为什么IFS=','好像完全没起作用?
这里你混淆了IFS和-d的作用,而且忽略了read分配变量的规则:
IFS的作用是字段分割,但只有当你给read传递多个变量时,它才会把读取到的内容按IFS拆分成字段,依次分配给各个变量。- 在最后一个循环里,你只给了
read一个变量name,不管你把IFS设成啥,read都会把整行读取到的内容(除了默认的换行终止符)全塞给这个变量。这里的输入是"Tom,Jack,Bill,",read默认按行读取(因为没指定-d,默认终止符是换行),所以整行内容包括所有逗号都被赋值给name,自然输出就是Tom,Jack,Bill,。 - 要是你改成
while IFS=',' read -r name1 name2 name3 name4; do,你就会看到name1=Tom、name2=Jack、name3=Bill、name4=(空字符串,因为最后一个逗号拆分出了空字段),这时候IFS的分割作用就体现出来了。
总结一下:read的行为取决于你用的选项(-a数组、-d自定义终止符)、变量数量,还有IFS的配合,不同组合的逻辑差异很大,得结合场景看~
备注:内容来源于stack exchange,提问作者dandan13
相关产品推荐
相关产品推荐

