在 bash 中运行,字符串作为命令不起作用(Duplicity)

在 bash 中运行,字符串作为命令不起作用(Duplicity)

我正在使用 Duplicity 将我的服务器备份到另一个服务器。

由于有多个服务器,我想创建一个按cron文件夹(和服务器)分离运行的 bash 脚本。为此,我在字符串中对命令进行了剪辑:

printf -v DulicityCMD 'duplicity --log-file %s --progress --no-encryption --include="/etc" --include="/var/log" --include="/var/db" --include="/var/www" --include="/home" --exclude="**" / scp://[email protected]:22//home/duplicity/backups/%s' $LOG_FILE $VPSNAME

echo $DulicityCMD

# THIS
$DulicityCMD

# OR THIS
sudo $DulicityCMD

$DulicityCMD如果你显示的内容echo将看起来像这样:

duplicity --log-file /var/log/duplicity/backup.log --progress --no-encryption --include="/etc" --include="/var/log" --include="/var/db" --include="/var/www" --include="/home" --exclude="**" / scp://[email protected]:22//home/duplicity/backups/myhost

当输入此命令时,正如所显示的那样echo,一切都运行正常。然而,当运行它时,变量,在内bash,它不起作用。

即使我用sudo或以 root 用户身份运行此程序,它也无法正常工作。错误的输出如下:

Traceback (most recent call last):
  File "/bin/duplicity", line 1546, in <module>
    with_tempdir(main)
  File "/bin/duplicity", line 1540, in with_tempdir
    fn()
  File "/bin/duplicity", line 1375, in main
    action = commandline.ProcessCommandLine(sys.argv[1:])
  File "/usr/lib64/python2.7/site-packages/duplicity/commandline.py", line 1131, in ProcessCommandLine
    set_selection()
  File "/usr/lib64/python2.7/site-packages/duplicity/commandline.py", line 969, in set_selection
    sel.ParseArgs(select_opts, select_files)
  File "/usr/lib64/python2.7/site-packages/duplicity/selection.py", line 268, in ParseArgs
    self.add_selection_func(self.glob_get_sf(arg, 1))
  File "/usr/lib64/python2.7/site-packages/duplicity/selection.py", line 434, in glob_get_sf
    sel_func = self.glob_get_filename_sf(glob_str, include)
  File "/usr/lib64/python2.7/site-packages/duplicity/selection.py", line 485, in glob_get_filename_sf
    raise FilePrefixError(filename)
FilePrefixError: "/etc"

当我使用 时eval,我没有收到此错误,但它会失去 root 权限,并且无法正常运行。因为我需要将此的输出与另一个命令 (sendmail) 结合使用|

这是什么问题?我该如何解决?

答案1

Stephen Rauch 的回答描述了问题。我有一个不同的解决方案。

但首先:为什么要将这么长的命令放入变量中?只是为了打印它?为什么不直接使用 shell 的详细(显示命令)选项?

设置-v
(你的命令)
设置 +v

(执行跟踪)选项x类似;尝试将上述选项与set -xset +x结合使用,看看有什么不同。如果您喜欢这两个选项,可以将它们与set -vx和结合起来set +vx


用空格分隔的一系列单词构建一个字符串,然后尝试将其拆分以恢复原始组成单词,这是一个非常糟糕的想法。您遇到了其中一个陷阱。另一个是文件名可能包含空格的理论可能性;从命令字符串中无法分辨出cat foo bar bazfoo bar是一个文件名还是baz另一个文件名(并且您已经看到构建字符串 ascat "foo bar" baz 也不起作用)。这些问题和其他问题在此处讨论:

此外,您不需要printf仅仅连接字符串。

如果您确实想在存储中构建命令行,并且使用 bash,则可以使用数组变量,如下所示:

DulicityCMD=(duplicity --log-file "$LOG_FILE" --progress --no-encryption --include="/etc" --include="/var/log" --include="/var/db" --include="/var/www" --include="/home" --exclude="**" / scp://[email protected]:22//home/duplicity/backups/"$VPSNAME")

请注意,我将$LOG_FILE$VPSNAME放在引号中。除非您有充分的理由不这样做,并且您确定自己知道自己在做什么,否则您应该始终引用所有对 shell 变量的引用。但 Stephen 是对的:您实际上不需要引用常量字符串,例如,,,"/etc"等等。(但当然,您"/var/log""/var/db"需要引用"**",因为*是特殊字符。

为了便于阅读,你可以将上面的内容分成多行。请注意,第一行是= 后面几行说+=,因为它们在之前的内容基础上添加了内容:

DulicityCMD=(duplicity --log-file "$LOG_FILE" --progress --no-encryption)
DulicityCMD+=(--include="/etc" --include="/var/log" --include="/var/db")
DulicityCMD+=(--include="/var/www" --include="/home" --exclude="**")
DulicityCMD+=(/ scp://[email protected]:22//home/duplicity/backups/"$VPSNAME")

然后你可以做

printf "%s\n" "${DulicityCMD[*]}"           (打印命令)
“${DulicityCMD[@]}”                         (执行命令)

或使用sudo "${DulicityCMD[@]}"(如果需要)。请参阅重击(1)有关 bash 语法的更多信息,以及我的答案在这里了解有关此阵列技术的更多信息。

答案2

问题:

这是字符串引用不正确的问题。当您从命令行运行以下命令时:

duplicity --include="/etc"

"/etc"被传递给底层程序,因为/etc"被 shell 剥离。但是当您执行如下操作时:

DuplicityCMD='duplicity --include="/etc"'
$DuplicityCMD

传递"/etc"的正是这个。这是因为,当你定义变量时DuplicityCMD,你明确要求将"包含在内'。我建议删除它们,"因为它们看起来基本上是不需要的。

更多信息:如何在 shell 中转义引号?

相关内容