Bash脚本由于pipping而调用subshel​​l

时间:2014-03-27 14:03:22

标签: linux bash shell scripting

假设我们有这个脚本

#!bin/bash
count=0
count2=0
find "$1" -type f | while read file; do
count=$(($count +1))
find "$1" -type f | while read file2; do
count2=$(($count2 +1))
done
done
echo "count: $count" # prints 0
echo "count2: $count2" # prints 0

现在让我们进行如下修改:

#!bin/bash
count=0
count2=0
find "$1" -type f | {
while read file; do
count=$(($count +1))
find "$1" -type f | {
while read file2; do
count2=$(($count2 +1))
done
}
done
echo "count: $count" # prints some number
echo "count2: $count2" # prints 0
}

我知道问题出在管道调用的子shell中。子shell中的变量只在子shell中更改,并且无法从主shell访问更改的值(或者至少与此类似)

所以我的问题是,有什么方法可以解决这个问题吗?以便count2打印其更改的结果? (除了写入和读取临时文件,或将代码更改为for循环)。 感谢。

3 个答案:

答案 0 :(得分:1)

您可以使用流程替换来避免管道,从而创建子shell:

#!bin/bash
count=0
count2=0

while read -r file; do
   ((count++))
   while read -r file2; do
      ((count2++))
   done < <(find "$1" -type f)
done < <(find "$1" -type f)

echo "count: $count"
echo "count2: $count2"

答案 1 :(得分:1)

不确定。避免管道;避免子壳:

#!bin/bash
count=0
count2=0
while read file; do
  count=$(($count +1))
  while read file2; do
    count2=$(($count2 +1))
  done < <(find "$1" -type f) 
done < <(find "$1" -type f) 
echo "count: $count"
echo "count2: $count2"

如果你有一个管道,如你所知,你有一个隐含的子shell。但是,我们只是从文件中重定向。 <(command)是进程替换 - 它使用给定进程的输出(或输入,如果使用>())创建命名管道或/ dev / fd文件句柄。然后它会扩展为文件名。

$ echo <(echo)
/dev/fd/63

因此,这个空间非常重要。这和

不一样
while read; do …; done << (command)

这只是语法错误!

我们正在做的更接近于此:

find "$1" -type f > tmpfile
while read; do …; done < tmpfile
rm tmpfile

除了文件创建和清理由系统处理。

当然,如果您只想计算文件:

find "$1" -type f -exec bash 'echo $#' _ {} + # Works up to ARG_MAX

# If number of files > ARG_MAX:
sum() {
  local IFS=+
  bc <<< "$*"
}
sum $( find "$1" -type f -exec bash 'echo $#' _ {} + )

答案 2 :(得分:1)

如果您使用的是bash 4,则可以取消对find和管道的调用。

#!bin/bash
count=0
count2=0
shopt -s globstar
for file in **/*; do
    [[ -f $file ]] || continue
    (( count++ ))
    for file2 in **/*; do
        [[ -f $file2 ]] || continue
        (( count2++ ))
    done
done

echo "count: $count" # prints some number
echo "count2: $count2" # prints 0