Warum verschiebt eine Dateiverschiebe-/Kopierfunktion bei Verwendung des Platzhalters „*“ immer nur eine Datei auf einmal?

Warum verschiebt eine Dateiverschiebe-/Kopierfunktion bei Verwendung des Platzhalters „*“ immer nur eine Datei auf einmal?
function mv1 { mv -n "$1" "targetdir" -v |wc -l ;}
mv1 *.png

Es verschiebt nur die erste .pnggefundene Datei, nicht alle.

Wie kann ich den Befehl auf alle Dateien anwenden, die mit den Platzhaltern übereinstimmen?

Antwort1

mv1 *.pngerweitert zuerst das Platzhaltermuster *.pngin die Liste der übereinstimmenden Dateinamen und übergibt dann diese Liste der Dateinamen an die Funktion.

Innerhalb der Funktion $1bedeutet das: Nehmen Sie das erste Argument der Funktion, teilen Sie es dort auf, wo es Leerzeichen enthält, und ersetzen Sie alle durch Leerzeichen getrennten Teile, die Platzhalterzeichen enthalten und mindestens einem Dateinamen entsprechen, durch die Liste der übereinstimmenden Dateinamen. Klingt kompliziert? Das ist es auch, und dieses Verhalten ist nur gelegentlich nützlich und oft problematisch. Dieses Aufteilungs- und Übereinstimmungsverhalten tritt nur auf, wenn $1es außerhalb von doppelten Anführungszeichen auftritt, daher ist die Lösung einfach: Verwenden Sie doppelte Anführungszeichen.Setzen Sie Variablensubstitutionen immer in Anführungszeichen.es sei denn, Sie haben einen guten Grund, dies nicht zu tun.

Wenn das aktuelle Verzeichnis beispielsweise die beiden Dateien A* algorithm.pngund enthält graph1.png, wird als erstes Argument und als zweites Argument an die Funktion mv1 *.pngübergeben . Dann wird in und aufgeteilt . Das Muster entspricht und enthält keine Platzhalterzeichen. Daher wird die Funktion letztendlich mit den Argumenten , , , und ausgeführt . Wenn Sie die Funktion korrigieren zuA* algorithm.pnggraph1.png$1A*algorithm.pngA*A* algorithm.pngalgorithm.pngmv-nA* algorithm.pngalgorithm.pngtargetdir-v

function mv1 { mv -n "$1" "targetdir" -v |wc -l ;}

dann wird die erste Datei korrekt verschoben.

Herstellenalledie Argumente, weisen Sie die Shell an, alle Argumente zu verarbeiten und nicht nur das erste. Sie können verwenden, "$@"um die vollständige Liste der an die Funktion übergebenen Argumente anzugeben.

function mv1 { mv -n "$@" "targetdir" -v |wc -l ;}

Dies ist fast korrekt, schlägt aber trotzdem fehl, wenn ein Dateiname mit dem Zeichen beginnt -, weil mvdieses Argument als Option behandelt wird. Übergeben Sie --es an mv, um ihm mitzuteilen „ab diesem Punkt keine weiteren Optionen mehr“. Dies ist eine sehr gängige Konvention, die von den meisten Befehlen unterstützt wird.

function mv1 { mv -n -v -- "$@" "targetdir" |wc -l ;}

Ein verbleibendes Problem ist mv, dass diese Funktion im Fehlerfall einen Erfolgsstatus zurückgibt, da der Beendigungsstatus von Befehlen auf der linken Seite einer Pipe ignoriert wird. In Bash (oder KSH) können Sie verwenden, set -o pipefailum die Pipeline zum Scheitern zu bringen. Beachten Sie, dass das Setzen dieser Option dazu führen kann, dass anderer Code, der in derselben Shell ausgeführt wird, fehlschlägt. Sie sollten sie daher lokal in der Funktion setzen, was seit Bash 4.4 möglich ist.

function mv1 {
  local -
  set -o pipefail
  mv -n -v -- "$@" "targetdir" | wc -l
}

In früheren Versionen pipefailwar die Einstellung fehleranfällig, daher wäre es besser, sie PIPESTATUSstattdessen explizit zu prüfen.

function mv1 {
  mv -n -v -- "$@" "targetdir" | wc -l
  ((!${PIPESTATUS[0] && !${PIPESTATUS[1]}}))
}

Antwort2

$1ist das erste Argument der Funktion, hier die erste Datei, die übereinstimmt *.png. Ich vermute, das "$@"ist es, was Sie anstelle von verwenden möchten $1.

Antwort3

Sie müssten verwenden mv1 \*.png.

Bei der Interaktion mit Funktionen übergibt das Linux-Terminal das Asterisk nicht direkt an den Befehl, sondern wählt den ersten passenden Parameter aus und übergibt diesen an den Befehl.

Um das Sternchen direkt durchzulassen, muss man es mit einem Backslash maskieren.

verwandte Informationen