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
~/Downloads
Sie 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, $HOME
auf 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 ls
in Skripten vermeiden. In diesem Fall müssen Sie beispielsweise nicht nach $1
einer Datei suchen, wenn es sich einfach um den Namen handelt . Außerdem ist die Verwendung aus verschiedenen Gründen problematisch:~/Downloads
grep $1
grep
wird 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.$1
ist nicht in Anführungszeichen gesetzt und kann daher Verwirrung stiften,grep
wenn es Leerzeichen oder andere Sonderzeichen enthält. Wenn$1
es ist*
, wird es beispielsweise auf alle Dateinamen im aktuellen Verzeichnis erweitert.- Wenn die Zeichenfolge
$1
mit einem Bindestrich beginnt, wird sie als Option für verwendetgrep
.
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 printf
Befehl 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, $name
den Aufruf in cp
der 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 .pdf
Dateien in , ~/Downloads
die die angegebene Zeichenfolge im Dateinamen enthalten. Jede dieser Dateien wird nach kopiert ~/Documents/books
.
Wenn Sie ein Unix-System verwenden, cp -v
das 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: