奇怪的bash脚本行为差异

时间:2015-07-08 15:07:01

标签: bash

#!/bin/bash
m=1
for m in {1..115};
do
     n=$(ls|grep gif|awk NR==$((m)))
     echo "${n}"
     echo "convert "${n}" -distort ScaleRotateTranslate 90 $((m)).gif"
     convert "${n}" -distort ScaleRotateTranslate 90 $((m)).gif
done

所以这是一个简单的bash脚本列出所有.gif扩展文件,然后转换它们,它用于回显和转换命令,并使用转换命令取消注释,但它被卡在第一个.gif名称并继续转换它直到循环结束(m正常递增)我认为可能是因为gif名称中的空格所以我决定调试它,当我回家时所以当我到家时我再次启动电脑我得到了一个完全不同的情况,如果我评论转换命令的行,n变量作为名称回应,这是我所期望的,但如果我取消注释它n被回应为循环时递增的数字。

非常感谢

1 个答案:

答案 0 :(得分:4)

尝试的问题是通过反复解析ls的输出引起的。来自ls的输出在循环之间不一致。每次循环时,您都会创建一个新的.gif文件,ls然后在下一次循环中将其包含在其输出中。如果新文件在原始文件之前排序,那么您将获得新文件n而不是您想要的原始文件。上面的glob循环没有那个问题,因为在创建任何新文件之前之前展开了glob。 shell首先展开glob ,然后在for循环中使用该文件名列表。

据说这是一个非常奇怪的,不必要的限制(除非它故意停在115个文件中)并且在当前目录中的所有.gif文件上操作效率极低。

您不需要$((m))来获取变量的值。您可以使用$m$((...))是一个算术表达式。如果你想做数学(你不是在这里)你需要它。

您不希望parse the output from ls它不安全或不可靠。

您的grep将匹配名称和扩展名中包含gif的文件。

如果要循环遍历当前目录中的所有*.gif文件,正确的方法是实际执行此操作。

for image in *.gif; do
    echo "Do something with $image"
done

在你的情况下看起来像这样(假设你想要数字输出文件)

m=1
for image in *.gif; do
    m=$((m+1))
    echo "convert ${image} -distort ScaleRotateTranslate 90 $m.gif"
    convert "${image}" -distort ScaleRotateTranslate 90 "$m.gif"
done

或者,正如kojiro正确指出的那样,你可以通过使用echo来让shell输出它运行的命令来避免可能不准确的set -x语句,并使用数组来避免保留自己的计数器< / p>

# The space here is important. It causes the glob expanded files to start at index 1 instead of 0 don't remove it.
images=([0]= *.gif)
set -x
for m in "${!images[@]}"; do
    convert "${images[m]}" -distort ScaleRotateTranslate 90 "$m.gif"
done
set +x