Mehrere Dateien per cp aus einem String kopieren

Mehrere Dateien per cp aus einem String kopieren

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, bashund cpscheine 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 cpglaube, 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 fVariable 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 readBefehl teilt die $varZeichenfolge 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 whileSchleifenkonstruktion, 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 bashManpage.

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/bashin 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.

verwandte Informationen