20.29 "while read line"循环内无法改变循环外变量
https://scz.617.cn/unix/201509231351.txt
Q:
!/bin/bash
count=0
"IFS="意在保持每行首尾两端的空白字符
参看read(1),-r表示将反斜杠\视为普通字符
ls -1aF /tmp | while IFS= read -r line do # # let count+=1 # count=$((count+1)) # ((count++)) # count=$(expr $count + 1 ) # # 第一种可移植性最高,最后一种效率最低 # count=$((count+1)) echo $count done
printf "Found %u entries\n" "${count}"
$ ./test.sh 1 2 ... 49 50 Found 0 entries
为什么最后显示0,而不是50?
A: scz 2015-09-23 13:51
对于bash下的shell script,管道符|会导致后续命令在子shell里运行,此时 "while read line"循环内的count改变发生在子进程里,当然不会影响父进程(当前 shell)里的count。要想在父子进程间同步count,必将涉及进程间通信。不是所有的 shell对管道符的处理都是创建子shell,比如ksh就不是。
解决方案之一:
!/bin/bash
count=0
while IFS= read -r line do ((count++)) echo $count
用"<(...)"这种子shell语法
done < <(ls -1aF /tmp)
printf "Found %u entries\n" "${count}"
该方案不用显式创建临时文件。实际上,<(...)子shell语法会在/tmp下隐式创建FIFO。
其他解决方案:
!/bin/bash
显式创建命名管道
rm -f /tmp/namedpipe mkfifo /tmp/namedpipe ls -1aF /tmp > /tmp/namedpipe &
count=0
while IFS= read -r line do ((count++)) echo $count done < /tmp/namedpipe
printf "Found %u entries\n" "${count}"
rm -f /tmp/namedpipe
!/bin/bash
{}构成command group
ls -1aF /tmp | { count=0 while IFS= read -r line do ((count++)) echo $count done printf "Found %u entries\n" "${count}" }
!/bin/bash
可以放在{}外
count=0
ls -1aF /tmp | { while IFS= read -r line do ((count++)) echo $count done printf "Found %u entries\n" "${count}" }
!/bin/bash
var=$(ls -1aF /tmp)
格式串中务必有\n。避免不以\n结尾的最后一行不被while read处理
printf "%s\n" "${var}" | { count=0 while IFS= read -r line do ((count++)) echo $count done printf "Found %u entries\n" "${count}" }
$ ./test.sh 1 2 ... 49 50 Found 50 entries
"IFS="用于"while read"时,意在保持每行首尾两端的空白字符,对比如下输出:
$ echo " this is a test " | while IFS= read -r line;do echo "[${line}]";done [ this is a test ] $ echo " this is a test " | while read -r line;do echo "[${line}]";done [this is a test]
"-r"用于"while read"时,意在取消每行内容中反斜杠\的转义效果,对比如下输出:
$ { echo 'this \ line is \';echo 'continued'; } | while IFS= read -r line;do echo "[${line}]";done [this \ line is ] [continued] $ { echo 'this \ line is \';echo 'continued'; } | while IFS= read line;do echo "[${line}]";done [this \ line is continued]
注意,{ ... }内部两端各有一个空格,必须存在。
一般写成"while IFS= read -r line",确保${line}是原始数据。
Q:
"while read line"循环如何从变量获取输入,而不是从管道或文件获取输入?
A: scz
!/bin/bash
var=$(ls -1aF /tmp)
count=0
while IFS= read -r line do ((count++)) echo $count
用<<<从变量获取输入,而不是<
搜"bash here string"了解更多细节
done <<< "${var}"
printf "Found %u entries\n" "${count}"
"bash here string"要求/tmp可写,会隐式创建临时文件。
另有一种类似的"here document":
!/bin/bash
var=$(ls -1aF /tmp)
count=0
while IFS= read -r line do ((count++)) echo $count
搜"bash here document"了解更多细节
后面的${var}两侧不要用双引号,不要写注释,否则视为文件内容
done << EOF ${var} EOF