我想计算给定目录中的所有* bin文件。最初我正在使用for-loop
:
var=0
for i in *ls *bin
do
perform computations on $i ....
var+=1
done
echo $var
但是,在某些目录中有太多文件导致错误:Argument list too long
因此,我正在尝试使用管道while-loop
:
var=0
ls *.bin | while read i;
do
perform computations on $i
var+=1
done
echo $var
现在的问题是使用管道子壳创建。因此,echo $var
会返回0
我该如何处理这个问题?
原始代码:
#!/bin/bash
function entropyImpl {
if [[ -n "$1" ]]
then
if [[ -e "$1" ]]
then
echo "scale = 4; $(gzip -c ${1} | wc -c) / $(cat ${1} | wc -c)" | bc
else
echo "file ($1) not found"
fi
else
datafile="$(mktemp entropy.XXXXX)"
cat - > "$datafile"
entropy "$datafile"
rm "$datafile"
fi
return 1
}
declare acc_entropy=0
declare count=0
ls *.bin | while read i ;
do
echo "Computing $i" | tee -a entropy.txt
curr_entropy=`entropyImpl $i`
curr_entropy=`echo $curr_entropy | bc`
echo -e "\tEntropy: $curr_entropy" | tee -a entropy.txt
acc_entropy=`echo $acc_entropy + $curr_entropy | bc`
let count+=1
done
echo "Out of function: $count | $acc_entropy"
acc_entropy=`echo "scale=4; $acc_entropy / $count" | bc`
echo -e "===================================================\n" | tee -a entropy.txt
echo -e "Accumulated Entropy:\t$acc_entropy ($count files processed)\n" | tee -a entropy.txt
答案 0 :(得分:45)
问题是while循环是在子shell中执行的。 while循环终止后,子shell的var
副本将被丢弃,并且父级的原始var
(其值未更改)将被回显。
解决此问题的一种方法是使用Process Substitution,如下所示:
var=0
while read i;
do
# perform computations on $i
((var++))
done < <(find . -type f -name "*.bin" -maxdepth 1)
请查看BashFAQ/024了解其他解决方法。
请注意,我还将ls
替换为find
,因为parse ls
不是一个好习惯。
答案 1 :(得分:8)
符合POSIX标准的解决方案是使用管道(p文件)。这个解决方案非常好用,便携式和POSIX,但在硬盘上写了一些东西。
mkfifo mypipe
find . -type f -name "*.bin" -maxdepth 1 > mypipe &
while read line
do
# action
done < mypipe
rm mypipe
您的管道是硬盘上的文件。如果您想避免使用无用的文件,请不要忘记将其删除。
答案 2 :(得分:1)
因此研究通用问题,将变量从带壳shell的while循环传递给父循环。我发现这里缺少的一种解决方案是使用here字符串。由于那是一个bash-ish,并且我更喜欢POSIX解决方案,所以我发现here-string实际上只是here-document的快捷方式。掌握了这些知识之后,我想到了以下内容,从而避免了使用subshell的麻烦:从而允许在循环中设置变量。
#!/bin/sh
set -eu
passwd="username,password,uid,gid
root,admin,0,0
john,appleseed,1,1
jane,doe,2,2"
main()
{
while IFS="," read -r _user _pass _uid _gid; do
if [ "${_user}" = "${1:-}" ]; then
password="${_pass}"
fi
done <<-EOT
${passwd}
EOT
if [ -z "${password:-}" ]; then
echo "No password found."
exit 1
fi
echo "The password is '${password}'."
}
main "${@}"
exit 0
所有复制粘贴程序的重要注意事项是,此处的文档是使用连字符设置的,指示将忽略制表符。这需要使布局保持良好。需要特别注意的是,因为stackoverflow不会在“代码”中呈现选项卡,而是将其替换为空格。 m所以,不要破坏我的代码,只是因为你们更喜欢空格而不是制表符,在这种情况下这无关紧要!
这可能会在不同的编辑器(设置)上中断,而不会中断。因此,替代方法是将其设置为:
done <<-EOT
${passwd}
EOT
答案 3 :(得分:-1)
这也可以通过for循环完成:
var=0;
for file in `find . -type f -name "*.bin" -maxdepth 1`; do
# perform computations on "$i"
((var++))
done
echo $var