为什么这个 Bash 脚本中的 diff 命令没有按我预期的那样工作?

为什么这个 Bash 脚本中的 diff 命令没有按我预期的那样工作?

我不明白为什么diff命令在我的脚本中无法正常工作。有人能帮我解释一下吗?

以下是 Bash 脚本:

#!/bin/bash

SRC="src-folder"
DST="dst-folder"
FILES=( a/ b/ c/ )

for f in ${FILES[@]}; do
  cmd="diff -q  $SRC/$f $DST/$f"
  echo $cmd  # row-9
  $cmd       # row-10
done

脚本输出如下:

diff -q src-folder/a/ dst-folder/a/
diff -q src-folder/a/ dst-folder/a/
diff -q src-folder/a/ dst-folder/a/
diff -q src-folder/a/ dst-folder/a/
diff -q src-folder/a/ dst-folder/a/
diff -q src-folder/a/ dst-folder/a/
diff -q src-folder/a/ dst-folder/a/
(until buffer overflow)

当我禁用/删除/注释第 10 行时,我可以看到正确的输出(当我在交互式 shell 中而不是在脚本中运行这些命令时,我得到了正确的结果):

diff -q src-folder/a/ dst-folder/a/
diff -q src-folder/b/ dst-folder/b/
diff -q src-folder/c/ dst-folder/c/

当我更改diff为时rsync,一切都正常工作(从脚本调用)。

那么我该如何在脚本中使用?我的脚本调用没有按预期工作diff的原因是什么?diff

我也尝试了一个非常简单的脚本:

#!/bin/bash

diff -q  src-folder/a dst-folder/a
diff -q  src-folder/b dst-folder/b

但它不起作用。当我将diff命令更改为任何其他命令(例如rsync)时,一切正常。

答案1

tl;dr:对我有用。

长答案:当我在我的一台测试机器上运行你问题的脚本时,它的行为完全符合预期:

a-schmidt@s-laugz2repo01:~$ ./show-diffs
diff -q src-folder/a/ dst-folder/a/
diff: src-folder/a/: No such file or directory
diff: dst-folder/a/: No such file or directory
diff -q src-folder/b/ dst-folder/b/
diff: src-folder/b/: No such file or directory
diff: dst-folder/b/: No such file or directory
diff -q src-folder/c/ dst-folder/c/
diff: src-folder/c/: No such file or directory
diff: dst-folder/c/: No such file or directory
a-schmidt@s-laugz2repo01:~$

因此,显然diff您系统上的命令与我的系统上的命令不同。这种情况有几种可能的原因,例如通过 shell 别名或本地安装同名程序。要找出原因,请输入 shell 命令:

type diff

在未修改的 Ubuntu 系统上这将输出:

diff is /usr/bin/diff

如果它输出其他内容,例如“diff 是别名......”或“diff 是一个函数......”,那么您需要查看 shell 配置文件以找出更改的位置,并可能进行更正。

答案2

我认为问题在于你的命令 cmd="diff -q $SRC/$f $DST/$f"选项扩展后第 10 行包含的字符串$cmd不是评估正确地作为 Bash 命令。

当您使用exec命令时,其中包含的字符串$cmd评估正确。但它exec的作用是将当前 shell 替换为执行命令的新 shell,因此第一次迭代后循环不再存在。

我已经强调评估几次,因为在 Bash 中eval命令就是这样做的 - 将字符串评估为命令。所以我认为正确的解决方案是像这样修改脚本:

eval $cmd       # row-10

最后要说的是,如果是整个脚本,为什么需要将命令分配给立即评估的变量(在下一行)?我的意思是,在这种特殊情况下,您可以通过以下方式简化循环:

for f in ${FILES[@]}; do
  diff -q  "$SRC/$f" "$DST/$f"
done

相关内容