Ich versuche, mehrere Dateien aus einem Shellscript in ein Verzeichnis zu kopieren. Diese Dateien enthalten alle möglichen „hässlichen“ Zeichen, wie Leerzeichen, Klammern und was nicht alles. Ich komme jedoch nicht weiter, wenn ich diese maskieren möchte, bash
und cp
scheine sie sehr seltsam zu handhaben.
Hier ist das Szenario:
Wenn ich diesen Befehl aus meiner Shell heraus eingebe, funktioniert er wie am Schnürchen:
cp /somedir/a\ file.png /somedir/another\ file.png /someotherdir/
Beim Lesen der zu kopierenden Dateien aus einem String wird es jedoch plötzlich merkwürdig: Ergebnisse in
var="/somedir/a\ file.png /somedir/another\ file.png"
cp "$var" /someotherdir/cp: cannot stat 'a\\ file.png another\\ file.png': No such file or directory
Ich glaube, das liegt daran, dass ich die Variable als String angebe und cp
glaube, es handele sich um eine Datei, obwohl es mehrere Dateien sind. Wenn ich denselben Befehl ausgebe, ohne die Variable in Anführungszeichen ( var="/somedir/a\ file.png /somedir/another\ file.png"; cp $var /someotherdir/
) zu setzen, erhalte ich einen noch seltsameren Fehler:
cp: cannot stat 'a\\ file.png another\\ file.png': No such file or directory
cp: cannot stat 'file.png': No such file or directory
cp: cannot stat 'another\\': No such file or directory
cp: cannot stat 'file.png': No such file or directory
Es scheint mein Entkommen völlig zu ignorieren. Was mache ich falsch?
BEARBEITEN:// Wie es scheintListe der Dateien kopierenhat eine Antwort mit xargs
, aber ich frage mich immer noch, warum sich Bash hier so seltsam verhält.
Antwort1
Doktor, es tut weh, wenn ich das mache …
Dann tu das nicht!
Im Ernst, warum versuchen Sie, einer einzelnen Variablen eine durch Leerzeichen getrennte Liste von Dateinamen zuzuweisen, die Leerzeichen enthalten? Das ist ein Rezept für eine Katastrophe.
Um Ihre implizite Frage zu beantworten: Ich stimme zu, dass die von Ihnen angezeigten Fehlermeldungen für die Befehle, die Sie verwendet haben, keinen Sinn ergeben.
Da Sie nicht gesagt haben, wie Sie bestimmen, welche Dateien kopiert werden sollen, ist es schwer zu wissen, welche Lösung für Sie funktioniert und welche nicht. Hier ist ein möglicher Ansatz:
︙ f1="/somedir/a datei.png" ︙ f2="/somedir/eine andere Datei.png" ︙ cp "$f1" "$f2" "$f3" … /einanderesVerzeichnis
/
(Das ist am Ende von nicht erforderlich /someotherdir/
, schadet aber auch nicht.) Dies wird dadurch eingeschränkt, dass Sie im Voraus die maximale Anzahl der zu kopierenden Dateien kennen müssen und eine Möglichkeit benötigen, um herauszufinden, welcher f
Variable Sie jede Datei zuweisen.
Dies kommt dem, was Sie jetzt tun, sehr nahe:
var="/somedir/a\ Datei.png /somedir/another\ Datei.png …" echo "$var" | während lesen f1 f2 f3 … Tun cp "$f1" "$f2" "$f3" … /einanderesVerzeichnis Erledigt
Der read
Befehl teilt die $var
Zeichenfolge an den Leerzeichen auf und entfernt die Backslashes, \
sodass nur
(Leerzeichen) übrig bleibt. Sie müssen jedoch trotzdem im Voraus wissen, wie viele Dateien Sie maximal kopieren müssen. (Sie benötigen die while
Schleifenkonstruktion, auch wenn diese Schleife nur einmal durchlaufen wird.)
Vielleicht kommt das hier der Sache nahe:
Dateiliste=/tmp/meine_Dateiliste.$$ ︙ echo "/somedir/a datei.png" >> "$dateiliste" ︙ echo "/somedir/eine andere Datei.png" >> "$filelist" ︙ während Dateiname gelesen wird Tun cp "$Dateiname" /einanderesVerzeichnis fertig < "$Dateiliste"
Eine weitere Variante ist die Verwendung eines variablen Arrays. Wie das geht, erfahren Sie auf der bash
Manpage.
Antwort2
Wenn Bash eine Befehlszeile analysiert, interpretiert es zuerst Anführungszeichen, Escapezeichen usw. und ersetzt dann Variablen. Wenn die Variablenwerte Escapezeichen, Anführungszeichen usw. enthalten, werden diese nie wirksam, da es zu spät ist, um die beabsichtigte Wirkung zu erzielen, wenn sie in den Befehl aufgenommen werden.
Im Allgemeinen besteht die beste Möglichkeit zur Lösung dieser Art von Problemen darin, die Dateinamen in einem Array (ein Element pro Dateiname) statt in einer Zeichenfolgenvariable zu speichern und dann "${array[@]}"
das Array zu erweitern, wobei jedes Element als separates „Wort“ behandelt wird:
files=(/somedir/a\ file.png /somedir/another\ file.png)
cp "${files[@]}" /someotherdir/
Update: Wenn Sie die Dateiliste dynamisch erstellen müssen, ist das mit einem Array ganz einfach:
files=() # Start with an empty list
for newfile in somethingorother; do
files+=("$newfile")
done
Antwort3
Wenn Sie Bash verwenden, können Sie den Namen der Datei in doppelte Anführungszeichen setzen, um die Verwendung von Escape-Zeichen zu vermeiden:
cp /old_location/This\ is\ an\ \[ugly\] \file.tmp /new_location/This\ is\ an\ \[ugly\] \file.tmp
verwenden Sie stattdessen Folgendes, wenn Sie es #!/bin/bash
in Ihrem Shellskript verwenden:
cp "/old_location/This is an [ugly] file with extras $$$.tmp" "/new_location/Ugly file with extras $$$.tmp"
In Ihrem Fall verwenden Sie die Werte innerhalb einer Variablen, übergeben die Variable jedoch falsch. Wenn eine Variable Leerzeichen enthält, sollten Sie diese ebenfalls in doppelte Anführungszeichen setzen:
var="/somedir/a\ file.png /somedir/another\ file.png"; cp "$var" /someotherdir/
Wenn Sie es nicht in doppelte Anführungszeichen setzen, ist es so, als würden Sie mehrere durch Leerzeichen getrennte Variablen verwenden.