Skip to content

Latest commit

 

History

History
227 lines (177 loc) · 4.78 KB

201509231351.txt.md

File metadata and controls

227 lines (177 loc) · 4.78 KB

20.29 "while read line"循环内无法改变循环外变量

http://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

printf "Found %u entries\n" "${count}"