ubuntuusers.de

Problem mit Leerzeichen / Variablen in rsync-Skript

Status: Gelöst | Ubuntu-Version: Kein Ubuntu
Antworten |

JaiBee

Avatar von JaiBee

Anmeldungsdatum:
8. Juni 2007

Beiträge: 1469

Hallo,

ich habe folgendes Skript (Beispiel) [ihr könnt es bei euch ohne Probleme ausführen, damit ihr das Problem besser versteht]:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[ "$#" == "0" ] &&  exit 1
mkdir -p /tmp/rsync/quelle1 "/tmp/rsync/quelle2 leerzeichen" /tmp/rsync/ziel

for i in "$@"; do test -d "$i" && ZIEL="$i"; done	# letzter Pfad als Zielverzeichnis
QUELLE="$(echo ${@%%$ZIEL})"				# $ZIEL herausfiltern

echo "Quelle(n):  $QUELLE"
echo "Ziel:       $ZIEL"
echo "- - - - - - - - - -"

rsync $QUELLE "$ZIEL" -savPbh --delete --numeric-ids

Ziel soll dabei folgendes sein: man übergibt mehrere Verzeichnisse an das Skript, welche mit rsync synchronisiert werden. Das letzte Verzeichnis ist das das Zielverzeichnis. Wundert euch nicht über die (in diesem Fall) umständliche Konstruktion mit der Schleife, denn in meinem „eigentlichen“ Skript ist es notwendig.

Folgender Aufruf erzeugt einen Fehler:

test.sh /tmp/rsync/quelle1 "/tmp/rsync/quelle2 leerzeichen" /tmp/rsync/ziel 
Quelle(n):  /tmp/rsync/quelle1 /tmp/rsync/quelle2 leerzeichen
Ziel:       /tmp/rsync/ziel
- - - - - - - - - -
sending incremental file list
rsync: link_stat "/tmp/rsync/quelle2" failed: No such file or directory (2)
rsync: link_stat "/home/janek/leerzeichen" failed: No such file or directory (2)

Die Ursache des Fehlers ist das Leerzeichen im zweiten „Quellverzeichnis“. Wenn man lediglich 1 Quelle hätte, könnte man das Problem schnell lösen, indem man $QUELLE in Zeile 11 mit Anführungszeichen umschließt. Da aber in diesem Fall mehrere Quellverzeichnisse angegeben werden, ist das nicht möglich (rsync würde mehrere Verzeichnisse als ein einziges ansehen).

Nun habe ich schon alles mögliche Ausprobiert: Escapen der Anführungszeichen und / oder des Leerzeichens oder z.B. $(echo $QUELLE). Ich vermute, dass rsync die escapten Anführungszeichen als Teil des Dateinamens interpretiert, was dann unweigerlich zu Fehlern führt.

Habt ihr eine Idee, wie man das Problem lösen kann? Ich zumindest, bin mit meinem Latein am Ende…

Vielen Dank!
Gruß JaiBee

theinlein

Anmeldungsdatum:
29. Dezember 2007

Beiträge: 1279

Kannst du nicht einfach "$@" übergeben?

rsync  "$@"  -savPbh --delete --numeric-ids

Grundsätzlich, wenn du die einzelnen Dateinamen jeweils in Anführungszeichen packst, müsste diese Notlösung gehen:

flist=""

for fil in "$@"
do
  flist="$flist \"$fil\" "
done

eval "rsync  $flist  $ZIEL  -savPbh --delete --numeric-ids"

John_W

Anmeldungsdatum:
10. Juli 2010

Beiträge: 571

Kannst du nicht einfach "$@" übergeben?

Da kann es passieren, das der Zielordner nochmal in sich selbst gesichert wird, wenn er nicht auf einen Slash endet; da gibt es aber auch Abhilfe:

target=
for i in "$@"
do
    target="$i"
done
rsync --exclude="$target" "$@" -savPbh --delete --numeric-ids "$target"

JaiBee

(Themenstarter)
Avatar von JaiBee

Anmeldungsdatum:
8. Juni 2007

Beiträge: 1469

theinlein schrieb:

Kannst du nicht einfach "$@" übergeben?

In dem obigen Beispiel würde das ausreichen, aber in meinem eigentlichen Skript ist das nicht möglich, da dort auch noch Optionen und Argumente übergeben werden (s.u.)

Grundsätzlich, wenn du die einzelnen Dateinamen jeweils in Anführungszeichen packst, müsste diese Notlösung gehen:

eval "rsync  $flist  $ZIEL  -savPbh --delete --numeric-ids"

Ah super, der Trick ist eval. Hatte so etwas Ähnliches bereits versucht, jedoch ohne eval.

Habe das Beispiel nun entsprechend angepasst:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
mkdir -p /tmp/rsync/quelle1 "/tmp/rsync/quelle2 leerzeichen" /tmp/rsync/ziel
[ "$#" == "0" ] &&  exit 1

for i in "$@"; do 	# letzte Pfad als Zielverzeichnis
	[ -d "$i" ] && ZIEL="$i";
done

for i in "$@"; do
	[[ -d "$i" && "$i" != "$ZIEL" ]] && QUELLE="$QUELLE \"$i\" "
done
echo -e "Quelle(n):  $QUELLE\nZiel:       $ZIEL"

eval "rsync $QUELLE "$ZIEL" -savPbh --delete --numeric-ids"

Das Skript muss auch noch funktionierten, wenn es etwa so ausgeführt wird (man beachte das -x y -abc):

test.sh -x y /tmp/rsync/quelle1 "/tmp/rsync/quelle2 leerzeichen" /tmp/rsync/ziel -abc 

Muss das morgen noch einmal mit meinem Hauptskript testen, aber das sieht schon gut aus. Lediglich die erste Schleife erscheint mir noch etwas umständlich.

Vielen Dank theinlein, du hast mir sehr geholfen!

Gruß

theinlein

Anmeldungsdatum:
29. Dezember 2007

Beiträge: 1279

... muss aber noch sagen

... eval ist sehr unschön, da es immer irgendwie eine Sicherheitslücke erzeugen kann:

Wenn ein gewiefter Böser dem Script statt Dateinamen tricky Code übergibt, wird der von eval hemmungslos ausgeführt.

John_W

Anmeldungsdatum:
10. Juli 2010

Beiträge: 571

Darf ich anmerken, dass meine Version da sicher arbeitet? Ich mein ja nur, das wurde irgendwie übergangen... 😉

JaiBee

(Themenstarter)
Avatar von JaiBee

Anmeldungsdatum:
8. Juni 2007

Beiträge: 1469

John W schrieb:

Ich mein ja nur, das wurde irgendwie übergangen... 😉

Sorry, war nicht böse gemeint. Deine Variante kommt für mein eigentliches Anliegen aber nicht in Frage, da sich nicht überprüfen lässt, was an das Skript übergeben wurde. Alles, was kein Verzeichnis ist, muss zuvor aus $@ herausgefiltert werden. Das macht die Sache so knifflig und ich würde auch gerne auf eval verzichten (vielleicht fällt ja noch jemandem dazu etwas ein).

Gruß

Antworten |