【问题标题】:Bash Variable Maths Not WorkingBash 变量数学不起作用
【发布时间】:2011-06-09 22:39:25
【问题描述】:

我有一个简单的 bash 脚本,它构成了我开发的内部网络应用程序的一部分。

它的目的是在用户删除原始图像时自动删除图像的缩略图。

脚本将一些基本状态信息记录到文件/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

虽然脚本正确删除了缩略图,但它没有正确输出正在清除的缩略图数,它始终显示0

例如,刚刚手动创建了一些孤立缩略图,然后运行我的脚本,手动生成的孤立缩略图被删除,但日志显示:

2011 年 6 月 9 日星期四 23:30:12 BST:0 个多余的拇指被删除

当文件正在被删除时,我做错了什么阻止 $filecounter 显示非零的数字。

我创建了以下 bash 脚本来测试它,它运行良好,输出 0 然后 1

#!/bin/bash
count=0
echo $count
count=$[$count+1]
echo $count

编辑:

感谢您的回答,但是为什么以下工作会起作用

$ x=3
$ x=$[$x+1]
$ echo $x
4

...第二个示例也有效,但在第一个脚本中不起作用?

第二次编辑:

这行得通

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

就像用count=$((count+1)) 替换count=$[$count+1] 一样,但不是在我的初始脚本中。

【问题讨论】:

    标签: linux bash math variables


    【解决方案1】:

    您使用了错误的运算符。尝试改用$(( ... )),例如:

    $ x=4
    $ y=$((x + 1))
    $ echo $y
    5
    $
    

    编辑
    您遇到的另一个问题是管道。之前遇到过这个问题(使用ksh,但我不会惊讶地发现其他外壳也有同样的问题)。管道正在分叉另一个 bash 进程,因此当您执行增量时,filcount 会在管道之后分叉的 subshel​​l 中递增。这个值不会传回调用shell,因为子shell有自己独立的环境(环境变量在被调用进程中继承,但被调用进程不能修改调用进程的环境)。

    作为一个例子,这表明文件计数可以增加:

    #!/bin/bash
    
    filecount=0
    ls /bin | while read x
    do
            filecount=$((filecount + 1))
            echo $filecount
    done
    echo $filecount
    

    ...所以您应该看到循环中的文件计数增加,但最终文件计数将为零,因为此回显属于主 shell,但分叉的子 shell(纯粹由 while 循环组成)。

    获取价值的一种方法是这样的......

    #!/bin/bash
    
    filecount=0
    filecount=`ls /bin | while read x
    do
            filecount=$((filecount + 1))
            echo $filecount
    done | tail -1`
    echo $filecount
    

    这仅在您不关心循环中的任何其他标准输出输出时才有效,因为这会将其全部丢弃,除了我们输出的最后一行(filecount 的最终值)。这是可行的,因为我们使用标准输出和标准输入将数据反馈给父 shell。

    根据您的观点,这要么是令人讨厌的 hack,要么是漂亮的 shell jiggery-pokery。我会让你决定你认为它是什么:-)

    【讨论】:

    • 好的,谢谢,我会试一试,但是为什么我的第二个示例可以工作呢?
    • 我已将文件修改为使用filecount=$((filecount+1)),但脚本仍会输出0,即使它已删除我放置的测试文件。
    • 不知道——它对我不起作用(我得到了字符串 $x[3+1] 的 x 的结果值),所以只能假设它是你的环境设置中的东西。跨度>
    • @Bryan -- 已编辑答案,因为它详细介绍了您所看到的内容。
    • @Chris:指出显而易见的事情做得很好,感谢您提醒我为什么改用 zsh! @Bryan:如果您称其为漂亮的 shell jiggery-pokery,那就是快乐的黑客行为。如果您希望您的第一个想法能够发挥作用,请将您的 hash-bang 切换到 /bin/zsh :)
    【解决方案2】:

    如果您将管道移除到 while 构造中,您就无需 bash 创建子 shell。

    改变这个:

    filecount=0
    find . -type f | while read file; do
      if [ ! -f "$imagepath/$file" ]; then
        filecount=$[$filecount+1]
        rm -f "$file"
      fi
    done
    echo $filecount
    

    到这里:

    filecount=0
    while read file; do
      if [ ! -f "$imagepath/$file" ]; then
        rm -f "$file" && (( filecount++ ))
      fi
    done < <(find . -type f)
    echo $filecount
    

    这更难阅读,因为find 命令在末尾隐藏。另一种可能是:

    files=$( find . -type f )
    while ...; do
      : 
    done <<< "$files"
    

    【讨论】:

      【解决方案3】:

      Chris J is quite right 您使用了错误的运算符和 POSIX subshel​​l 变量范围意味着您无法以这种方式获得最终计数。

      附带说明,在进行数学运算时,您还可以考虑使用let shell bultin,如下所示:

      $ filecount=4
      $ let filecount=$filecount+1
      $ echo $filecount
      5
      

      此外,如果您希望范围界定能够像您期望的那样工作,尽管有该管道,您可以使用 zsh 而不是 bash。在这种情况下,它应该是替换并按预期工作。

      【讨论】:

      • 我仍然得到0 的输出,我也尝试了你的方法。我的脚本是逐字记录的(复制和粘贴),所以没有其他干扰。
      • 这并不奇怪。这在 ZSH 上工作,但在 Bash 中,变量一直计数,直到它跳出 while 循环,然后突然又变成 0。在脚本开头使用set -x 进行查看。 let 语法很好,但复制你的循环结构会在最后重置 var?!?!
      • 见我上面的回答:这都是关于管道如何工作的。有些 shell 很聪明,可以在内部进行管道传输。其他人只是分叉一个新进程。您正在看到管道分叉进程的结果。 ksh 对此感到很头疼,因为一些实现足够聪明,可以在不分叉的情况下做事,而其他实现则分叉一个新的 shell。最安全的假设是,一旦您开始管道,您将管道进入一个独立的进程,因此父环境永远不会改变。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-25
      • 1970-01-01
      相关资源
      最近更新 更多