20.29 "while read line"循环内无法改变循环外变量
http://scz.617.cn/unix/201509231351.txt
Q:
#!/bin/bash
count=0
ls -1aF /tmp | while IFS= read -r line
do
#
# let count+=1
# count=$((count+1))
# ((count++))
# count=$(expr
$ ./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
done < <(ls -1aF /tmp)
该方案不用显式创建临时文件。实际上,<(...)子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}"
#!/bin/bash
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)
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
done <<< "${var}"
"bash here string"要求/tmp可写,会隐式创建临时文件。
另有一种类似的"here document":
#!/bin/bash
var=$(ls -1aF /tmp)
count=0
while IFS= read -r line do ((count++)) echo $count
done << EOF ${var} EOF