Bash 变量数学不起作用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6300137/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Bash Variable Maths Not Working
提问by Bryan
I have a simple bash script, which forms part of an in house web app that I've developed.
我有一个简单的 bash 脚本,它是我开发的内部网络应用程序的一部分。
It's purpose is to automate deletion of thumbnails of images when the original image has been deleted by the user.
它的目的是在用户删除原始图像时自动删除图像的缩略图。
The script logs some basic status info to a file /var/log/images.log
该脚本将一些基本状态信息记录到文件中 /var/log/images.log
#!/bin/bash
cd $thumbpath
filecount=0
# Purge extraneous thumbs
find . -type f | while read file
do
if [ ! -f "$imagepath/$file" ]
then
filecount=$[$filecount+1]
rm -f "$file"
fi
done
echo `date`: $filecount extraneous thumbs removed>>/var/log/images.log
Whilst the script correctly deletes thumbs, it doesn't correctly output the number of thumbs that are being purged, it always shows 0.
虽然脚本正确删除了拇指,但它没有正确输出正在清除的拇指数,它始终显示0.
For example, having just manually created some orphaned thumbnails, and then running my script, the manually generated orphaned thumbs are deleted, but the log shows:
例如,刚刚手动创建了一些孤立缩略图,然后运行我的脚本,手动生成的孤立缩略图被删除,但日志显示:
Thu Jun 9 23:30:12 BST 2011: 0 extraneous thumbs removed
2011 年 6 月 9 日星期四 23:30:12 BST:0 多余的拇指被移除
What am I doing wrong that is stopping $filecounter from showing a number other than zero, when files arebeing deleted.
我在做什么错的是从显示大于零,当文件等多项停止$ filecounter都被删除。
I've created the following bash script to test this, and this works perfectly, outputting 0then 1:
我已经创建了以下 bash 脚本来测试这个,这很好用,0然后输出1:
#!/bin/bash
count=0
echo $count
count=$[$count+1]
echo $count
Edit:
编辑:
Thanks for the answers, but why does the following work
感谢您的回答,但为什么以下工作
$ x=3
$ x=$[$x+1]
$ echo $x
4
...and also the second example works, yet it doesn't work in the first script?
...第二个例子也有效,但它在第一个脚本中不起作用?
Second Edit:
第二次编辑:
This works
这有效
count=0
echo Initial Value $count
for i in `seq 1 5`
do
count=$[$count+1]
echo $count
done
echo Final Value $count
Initial Value 0
1
2
3
4
5
Final Value 5
as does replacing count=$[$count+1]with count=$((count+1)), but not in my initial script.
替换count=$[$count+1]为count=$((count+1)),但不在我的初始脚本中。
回答by Chris J
You're using the wrong operator. Try using $(( ... ))instead, e.g.:
您使用了错误的运算符。尝试使用$(( ... )),例如:
$ x=4
$ y=$((x + 1))
$ echo $y
5
$
EDIT
The other problem you're bumping into is down to the pipe. Bumped into this one before (with ksh, but wouldn't suprise me to find that other shells have the same problem). The pipe is forking another bash process, so when you do the increment, filcount is getting incremented in the subshellthat's been forked after the pipe. This value isn't passed back to the calling shell as the subshell has it's own independent environment (environment variables are inherited in called processes, but called process cannot modify the environment of the calling process).
编辑
您遇到的另一个问题是管道。之前碰到过这个(使用ksh,但我不会惊讶地发现其他外壳也有同样的问题)。管道正在分叉另一个 bash 进程,因此当您进行增量时,filcount在管道后分叉的子外壳中增加。这个值不会传回调用shell,因为子shell有它自己独立的环境(环境变量在被调用进程中继承,但被调用进程不能修改调用进程的环境)。
As an example, this demonstrates that filecount gets incremented okay:
作为一个例子,这表明文件计数增加了:
#!/bin/bash
filecount=0
ls /bin | while read x
do
filecount=$((filecount + 1))
echo $filecount
done
echo $filecount
...so you should see filecount increase in the loop, but the final filecount will be zero because this echo belongs to the main shell, but the forked subshell (which consists purely of the while loop).
...所以您应该看到循环中的文件计数增加,但最终文件计数将为零,因为此回显属于主 shell,但属于分叉的子 shell(纯粹由 while 循环组成)。
One way you can get the value back is like this...
一种可以取回价值的方法是这样的......
#!/bin/bash
filecount=0
filecount=`ls /bin | while read x
do
filecount=$((filecount + 1))
echo $filecount
done | tail -1`
echo $filecount
This will only work if you don't care about any other stdout output in the loop as this throws it all away apart from the last line we output (the final value of filecount). This works because we're using stdout and stdin to feed the data back to the parent shell.
这仅在您不关心循环中的任何其他 stdout 输出时才有效,因为这会将其全部抛离我们输出的最后一行( 的最终值filecount)。这是有效的,因为我们使用 stdout 和 stdin 将数据反馈给父 shell。
Depending on your viewpoint this is either a nasty hack or a nifty bit of shell jiggery-pokery. I'll leave you to decide what you think it is :-)
根据你的观点,这要么是一个令人讨厌的黑客,要么是一个漂亮的 shell jiggery-pokery。我会让你决定你认为它是什么:-)
回答by glenn Hymanman
If you remove the pipeline into the whileconstruct, you remove bash's need to create a subshell.
如果您将管道移除到while构造中,您将移除 bash 创建子 shell 的需要。
Change this:
改变这个:
filecount=0
find . -type f | while read file; do
if [ ! -f "$imagepath/$file" ]; then
filecount=$[$filecount+1]
rm -f "$file"
fi
done
echo $filecount
to this:
对此:
filecount=0
while read file; do
if [ ! -f "$imagepath/$file" ]; then
rm -f "$file" && (( filecount++ ))
fi
done < <(find . -type f)
echo $filecount
That is harder to read because the findcommand is hidden at the end. Another possibility is:
这更难阅读,因为find命令隐藏在最后。另一种可能是:
files=$( find . -type f )
while ...; do
:
done <<< "$files"
回答by Caleb
Chris J is quite rightthat you are using the wrong operator and POSIX subshell variable scoping means you can't get a final count that way.
Chris J 说得对,您使用了错误的运算符,而 POSIX 子shell 变量范围意味着您无法以这种方式获得最终计数。
As a side note, when doing math operations you could also consider using the letshell bultin like this:
作为旁注,在进行数学运算时,您还可以考虑使用letshell bultin,如下所示:
$ filecount=4
$ let filecount=$filecount+1
$ echo $filecount
5
Also if you want scoping to just work like you expected it to in spite of that pipeline, you could use zsh instead of bash. In this case it should be a drop in replacement and work as expected.
此外,如果您希望作用域按您预期的那样工作,尽管有该管道,您可以使用 zsh 而不是 bash。在这种情况下,它应该减少替换并按预期工作。

