Ausführen in Bash, String als Befehl funktioniert nicht (Duplicity)

827
guinalz

Ich verwende Duplicity, um meinen Server auf einem anderen zu sichern.

Da es mehrere Server gibt, möchte ich ein Bash-Skript erstellen, das mit einer cronTrennung nach Ordnern (und Servern) ausgeführt wird. Dafür habe ich die Montage des Befehls in string gemacht:

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://duplicity@myhost.com:22//home/duplicity/backups/%s' $LOG_FILE $VPSNAME  echo $DulicityCMD  # THIS $DulicityCMD  # OR THIS sudo $DulicityCMD 

Wenn Sie den Inhalt der Anzeige $DulicityCMDmit echowird es in etwa so aussehen:

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://duplicity@myhost.com:22//home/duplicity/backups/myhost 

Wenn Sie diesen Befehl eingeben echo, funktioniert alles genau wie das, was angezeigt wird. Wenn es jedoch von der Variablen aus ausgeführt wird bash, funktioniert es nicht.

Selbst wenn ich dies mit sudooder als root-Benutzer ausführte, funktioniert es nicht. Die Ausgabe des Fehlers lautet wie folgt:

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" 

Wenn ich verwende eval, erhalte ich diesen Fehler nicht, aber er verliert Root-Rechte und wird nicht ordnungsgemäß ausgeführt. Weil ich die Ausgabe davon in Verbindung mit einem anderen Befehl (sendmail) verwenden muss |.

Was ist das Problem und wie kann ich es beheben?

0
Siehe [BashFAQ # 50: Ich versuche, einen Befehl in eine Variable einzufügen, aber die komplexen Fälle schlagen immer fehl!] (Http://mywiki.wooledge.org/BashFAQ/050). Gordon Davisson vor 7 Jahren 0

2 Antworten auf die Frage

0
Stephen Rauch

Problem:

Dies ist ein Problem der richtigen Zeichenfolge. Wenn Sie von der Kommandozeile aus etwas ausführen:

duplicity --include="/etc" 

Der "/etc"Wille wird an das zugrunde liegende Programm übergeben, /etcda die "von der Shell entfernt wurde. Aber wenn du so etwas machst:

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

Das "/etc"wird genau so übergeben. Dies liegt daran, dass Sie beim Definieren Ihrer Variablen DuplicityCMDexplizit darum gebeten haben, dass die Variable eingeschlossen wird, indem Sie sie in "einschließen '. Ich würde vorschlagen, die zu entfernen, "da sie weitgehend unnötig erscheinen.

( Weitere Informationen: Wie kann man Anführungszeichen in Shell entziehen? )

(1) Versuchen Sie es erneut. Der Befehl, den Sie anzeigen, führt einen Befehl namens --log-file mit den Argumenten $ LOG_FILE aus --progress --no-encrypt --include = / etc --include = / var / log --include = / var / db --include = / var / www --include = / home --exclude = '**' / scp: //duplicity@myhost.com: 22 // home / duplicity / backups / $ VPSNAM` und Umgebungsvariable "DuplicityCMD" auf "Duplizität" gesetzt. (2) Wenn eine Person ein Problem mit Anführungszeichen hat, schlagen Sie bitte nicht vor, die Anführungszeichen zu entfernen, außer als letzter Ausweg. G-Man vor 7 Jahren 0
@ G-Man 1) Danke, 2) Was? Stephen Rauch vor 7 Jahren 0
Was? Wie in [Auswirkungen auf die Sicherheit des Vergissens, eine Variable in Bash / POSIX-Shells zu zitieren] (// unix.stackexchange.com/q/171346/80216) diskutiert, gibt es hier auf [SU] und [Unix & Linux Stack Exchange] (// unix.stackexchange.com) von Leuten, die nicht Dinge zitieren, die sie zitieren sollten - z. B. zitierte guinalz nicht $ LOG_FILE und $ VPSNAME. Ihre Antwort sieht jedoch so aus, als würden Sie sagen: „Dies ist ein Problem der richtigen String-Anführungszeichen. Ich würde vorschlagen, das "" "zu entfernen. Wir möchten den Leuten nicht beibringen, Zitate zu entfernen, wenn sie ein Problem haben. G-Man vor 7 Jahren 0
PS Ihr Befehl schlägt jetzt aufgrund von --exclude = '**' `fehl. G-Man vor 7 Jahren 0
@ G-Man. Danke für die Info, hilfreich. Noch mehr von der Überlieferung dieses Ortes. Nur einen Monat hier gewesen, interessant gewesen. Stephen Rauch vor 7 Jahren 0
0
G-Man

Stephen Rauch's Antwort beschreibt das Problem. Ich habe eine andere Lösung.

Aber zuerst: Warum setzen Sie einen so langen Befehl in eine Variable? Nur damit Sie es ausdrucken können? Warum nicht einfach die verbose-Option (Befehle anzeigen) der Shell verwenden?

set -v (dein_Befehl) set + v

Die xOption (Ablaufverfolgung) ist ähnlich. Versuchen Sie es mit set -xund set +x, um den Unterschied zu sehen. Wenn Sie beide mögen, können Sie sie mit set -vxund kombinieren set +vx.


Es ist eine ziemlich schlechte Idee, eine Zeichenfolge aus einer Folge von Wörtern zu erstellen, die durch Leerzeichen getrennt sind, und dann zu versuchen, sie zu trennen, um die ursprünglichen Wortbestandteile wiederherzustellen. Sie haben eine der Fallen getroffen. Ein weiterer Grund ist die theoretische Möglichkeit, dass ein Dateiname Leerzeichen enthält. Es ist unmöglich, an der Befehlszeichenfolge zu erkennen, cat foo bar baz dass foo bares sich um einen Dateinamen und einen bazanderen handelt (und Sie haben gesehen, dass das Erstellen der Zeichenfolge ebenso cat "foo bar" baz nicht gut funktioniert). Diese und andere Fragen werden hier diskutiert:

Sie müssen nicht printfnur Strings verketten.

Wenn Sie Ihre Befehlszeile wirklich im Speicher erstellen möchten und bash verwenden, können Sie eine Array-Variable wie folgt verwenden:

DulicityCMD=(duplicity --log-file "$LOG_FILE" --progress --no-encryption --include="/etc" --include="/var/log" --include="/var/db" --include="/var/www" --include="/home" --exclude="**" / scp://duplicity@myhost.com:22//home/duplicity/backups/"$VPSNAME") 

Beachten Sie, dass ich setzen $LOG_FILEund $VPSNAMEin Anführungszeichen. Sie sollten immer alle Verweise auf Shell-Variablen angeben, es sei denn, Sie haben einen guten Grund, dies nicht zu tun, und wissen, was Sie tun. Aber Stephen hat Recht: Sie nicht wirklich brauchen, wie konstante Strings zitieren "/etc", "/var/log", "/var/db", und so weiter. (Aber natürlich, Sie tun müssen, zitieren "**", weil *ein Sonderzeichen ist.)

Zur besseren Lesbarkeit können Sie die obigen Zeilen in mehrere Zeilen aufteilen. Beachten Sie, dass in der ersten Zeile = und in den nachfolgenden Zeilen darauf hingewiesen wird +=, dass sie das Vorherige hinzufügen:

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://duplicity@myhost.com:22//home/duplicity/backups/"$VPSNAME") 

Dann kannst du es tun

printf "% s \ n" "$ " (um den Befehl zu drucken) "$ " (um den Befehl auszuführen)

oder verwenden Sie sudo "$", wenn Sie brauchen. Weitere Informationen zur Bash-Syntax finden Sie in bash (1). In meiner Antwort finden Sie weitere Informationen zu dieser Array-Technik.