7

记一次隐含子shell引发的问题

 2 years ago
source link: https://www.lujun9972.win/blog/2017/11/30/%E8%AE%B0%E4%B8%80%E6%AC%A1%E9%9A%90%E5%90%AB%E5%AD%90shell%E5%BC%95%E5%8F%91%E7%9A%84%E9%97%AE%E9%A2%98/index.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

记一次隐含子shell引发的问题

最近在写一个shell的时候发现了个奇怪的现象,有一个数组在 while 循环内是有内容的,然而出了 while 循环就又变成空值了.

代码示意如下:

declare -a files
i=0
find ./ -name "d*.org" |while read file;do
    files[$i]="$file"
    i=$((i+1))
    echo 1. count = ${#files[@]}
done
echo 2. count = ${#files[@]}

输出结果为:

1. count = 1
1. count = 2
2. count = 0

这就很神奇了. 在咨询了群里的大神之后才知道,原来当通过管道传送内容给一个循环中时,这个循环会隐式地在一个 子shell 中执行. 所以while中修改的其实是 子shell 中的 files. 而跳出这个 while 循环后,输出的是 父shell 中的 files.

其实这个问题可以通过 shellcheck 检测出来:

shellcheck /tmp/test.sh
n test.sh line 4:
find ./ -name "2*.org" |while read file;do
                              ^-- SC2162: read without -r will mangle backslashes.


In test.sh line 5:
    files[$i]="$file"
    ^-- SC2030: Modification of files is local (to subshell caused by pipeline).


In test.sh line 9:
echo 2. count = ${#files[@]}
                ^-- SC2031: files was modified in a subshell. That change might be lost.

正确的写法应该是:

declare -a files
i=0
while read file;do
    files[$i]="$file"
    i=$((i+1))
    echo 1. count = ${#files[@]}
done< <(find ./ -name "d*.org")
echo 2. count = ${#files[@]}

注意: done 后面不是 << (here document),而是 < <(command), 意思是将 command 的结果重定向给 while 循环.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK