cp mit Leerzeichen und Sonderzeichen funktioniert auf dem Terminal einwandfrei, aber nicht im Skript

cp mit Leerzeichen und Sonderzeichen funktioniert auf dem Terminal einwandfrei, aber nicht im Skript

Ich versuche, ein Skript zu schreiben, um eine Datei zu finden und in einen bestimmten Ordner zu kopieren. Der Dateiname enthält Leerzeichen und andere Sonderzeichen.

a=$(ls ~/Downloads/ | grep $1)
a="'$a'"
a="~/Downloads/"$a
echo $a
cp $a ~/Documents/books/.

Wenn ich den Dateinamen in Anführungszeichen setze, funktioniert es auf dem Terminal einwandfrei. Aber im Skript werden die Wörter als separate Argumente behandelt. Im obigen Skript gibt es bei der Ausgabe von $a etwa Folgendes aus:

~/Downloads/'Ian Goodfellow, Yoshua Bengio, Aaron Courville - Deep Learning [pre-pub version] (2016, MIT Press).pdf'

Sie können sehen, dass der Dateiname in Anführungszeichen steht. Wenn das obige Argument auf dem Terminal an cp übergeben wird, funktioniert es einwandfrei, aber nicht im Skript. Bitte helfen Sie.

Antwort1

~/DownloadsSie möchten anscheinend eine bestimmte Datei von nach kopieren ~/Documents/books.

Als erstes ist zu beachten, dass ~sich nicht wie eine Variable verhält. Insbesondere verhält es sich nicht wie eine Variable.nichtwird in Anführungszeichen zum Pfad Ihres Home-Verzeichnisses erweitert. In Skripten ist es daher besser, $HOMEauf das Home-Verzeichnis zu verweisen.

Zweitens scheinen Sie dem Wert Ihrer Variable wörtliche einfache Anführungszeichen hinzuzufügen $a. Dies ist nicht notwendig und führt nur zu einem Fehler „Keine solche Datei oder kein solches Verzeichnis“ (weil der Dateiname keine einfachen Anführungszeichen enthält).

Drittens sollten Sie die Verwendung von lsin Skripten vermeiden. In diesem Fall müssen Sie beispielsweise nicht nach $1einer Datei suchen, wenn es sich einfach um den Namen handelt . Außerdem ist die Verwendung aus verschiedenen Gründen problematisch:~/Downloadsgrep $1

  • grepwird hauptsächlich für Text verwendetUnterlagen. Dateinamen können theoretisch Zeilenumbrüche enthalten, weshalb das „Greppen“ von Dateinamen nur funktioniert, wenn man weiß, dass die Namen „schön“ sind.
  • $1ist nicht in Anführungszeichen gesetzt und kann daher Verwirrung stiften, grepwenn es Leerzeichen oder andere Sonderzeichen enthält. Wenn $1es ist *, wird es beispielsweise auf alle Dateinamen im aktuellen Verzeichnis erweitert.
  • Wenn die Zeichenfolge $1mit einem Bindestrich beginnt, wird sie als Option für verwendet grep.

Ihr Skript kann also wie folgt geschrieben werden:

#!/bin/sh

name="$HOME/Downloads/$1"

printf 'The filename is "%s"\n' "$name"

cp "$name" "$HOME/Documents/books"

Der printfBefehl hier soll den Namen der Datei ausgeben, die wir ausgewählt haben. Ich binAusgabeAnführungszeichen um den Namen, aber der Namenichthabe doppelte Anführungszeichen darin. Stattdessen achte ich darauf, $nameden Aufruf in cpder letzten Zeile in Anführungszeichen zu setzen. Tatsächlich achte ich darauf,alleErweiterungen, von denen ich weiß, dass sie sonst dazu führen würden, dass die Shell den Wert der Variablen aufspaltet.

Sie würden dieses Skript verwenden als

./script.sh 'Ian Goodfellow, Yoshua Bengio, Aaron Courville - Deep Learning [pre-pub version] (2016, MIT Press).pdf'

Der Name der Datei muss in Anführungszeichen gesetzt werden, da er Leerzeichen und Zeichen enthält, die in der Shell sonst speziell sind.

Wenn Sie möchten, dass Ihr Skript nach einer Datei sucht, die ein in der Befehlszeile eingegebenes Wort enthält, so dass Sie sagen könnten

./script.sh Goodfellow

Verwenden Sie dann die angegebene Zeichenfolge in einem Globbing-Muster:

#!/bin/sh

for name in "$HOME/Downloads"/*"$1"*.pdf; do
    printf 'Will copy the file "%s"\n' "$name"
    cp "$name" "$HOME/Documents/books"
done

Jetzt nimmt das Skript das erste Argument und durchläuft alle .pdfDateien in , ~/Downloadsdie die angegebene Zeichenfolge im Dateinamen enthalten. Jede dieser Dateien wird nach kopiert ~/Documents/books.

Wenn Sie ein Unix-System verwenden, cp -vdas Dateien ausführlich kopiert, kann das Skript möglicherweise noch weiter verkleinert werden:

#!/bin/sh

cp -v "$HOME/Downloads"/*"$1"*.pdf "$HOME/Documents/books"

Dies verhindert, dass Sie die Ausgabe selbst formatieren („Kopiert …“), da wir nicht mehr über die Dateinamen schleifen, die dem angegebenen Muster entsprechen. Stattdessen verlassen wir uns einfach darauf, dass, wenn Sie mehrere Dateinamen angeben cp(und das Globbing-Muster kann durchaus auf mehrere Dateinamen erweitert werden), diese mit einem einzigen Befehl in ein Zielverzeichnis kopiert werden können.


Siehe auch:

verwandte Informationen