Gibt es eine Möglichkeit, "mv" stillschweigend zum Scheitern zu bringen?


61

Ein Befehl wie mv foo* ~/bar/erzeugt diese Nachricht in stderr, wenn keine passenden Dateien vorhanden sind foo*.

mv: cannot stat `foo*': No such file or directory

In dem Skript, an dem ich arbeite, wäre dieser Fall jedoch völlig in Ordnung, und ich würde diese Nachricht gerne aus unseren Protokollen streichen.

Gibt es eine nette Möglichkeit, mvruhig zu sein, auch wenn nichts bewegt wurde?


13
mv foo* ~/bar/ 2> /dev/null?
Thomas Nyman

1
Gut ja. Ich denke, ich hatte etwas "Schöneres" im Sinn, wie einen Schalter für mv. :) Aber das reicht natürlich.
Jonik

3
Ich habe gesehen, dass einige mvImplementierungen eine -qOption unterstützen, um sie zu beruhigen, aber das ist nicht Teil der POSIX-Spezifikation für mv. Die mvin GNU coreutils, zum Beispiel, hat nicht haben eine solche Option.
Thomas Nyman

Nun, ich glaube nicht, dass das Problem darin besteht, wie man "mv" ruhig hält, aber wie man es besser macht. Überprüfen Sie, ob eine Datei / dir foo * vorhanden ist und der Benutzer über eine beschreibbare Berechtigung verfügt. Führen Sie schließlich möglicherweise "mv" aus.
Shâu Shắc

Antworten:


46

Suchen Sie das?

$ mv  file dir/
mv: cannot stat file’: No such file or directory
$ mv  file dir/ 2>/dev/null
# <---- Silent ----->

20
Beachten Sie, dass der Rückgabewert immer noch! = 0 ist. Daher schlägt jeder set -e(der in jedem Shell-Skript verwendet werden sollte) fehl. Sie können ein hinzufügen || true, um die Prüfung für einen einzelnen Befehl zu deaktivieren.
Reto

10
@reto set -esollte nicht in jedem Shell-Skript verwendet werden. In vielen Fällen wird das Fehlermanagement dadurch noch komplexer als ohne.
Chris Down

1
@ ChrisDown Ich verstehe deinen Standpunkt. Es ist normalerweise eine Wahl zwischen zwei Übeln. Aber im Zweifelsfall bevorzuge ich einen fehlgeschlagenen erfolgreichen Lauf als einen erfolgreichen Fehlschlag. (was erst bemerkt wird, wenn es für einen Kunden / Benutzer sichtbar wird). Shell-Skripte sind ein großes Minenfeld für Anfänger, es ist so einfach, einen Fehler zu übersehen, und Ihr Skript geht durcheinander!
Reto

14

Eigentlich halte ich Muting nicht für mveinen guten Ansatz (denken Sie daran, es könnte Sie auch über andere Dinge informieren, die von Interesse sein könnten ... zB fehlende ~/bar). Sie möchten es nur stumm schalten, wenn Ihr Glob-Ausdruck keine Ergebnisse liefert. Eigentlich lieber gar nicht ausführen.

[ -n "$(shopt -s nullglob; echo foo*)" ] && mv foo* ~/bar/

Sieht nicht sehr ansprechend aus und funktioniert nur in bash.

ODER

[ 'foo*' = "$(echo foo*)" ] || mv foo* ~/bar/

nur außer du bist bashmit nullglobset dabei. Sie zahlen jedoch einen Preis von 3x Wiederholung des Glob-Musters.


Kleinere Probleme mit der zweiten Form: Eine Datei mit dem Namen kann nicht verschoben werden, foo*wenn sie die einzige im aktuellen Verzeichnis ist, die mit dem Glob übereinstimmt foo*. Dies kann mit einem Glob-Ausdruck umgangen werden, der sich nicht im wahrsten Sinne des Wortes anpasst. Eg [ 'fo[o]*' = "$(echo fo[o]*)" ] || mv fo[o]* ~/bar/.
Freitag,

13

find . -maxdepth 1 -name 'foo*' -type f -print0 | xargs -0r mv -t ~/bar/

- GNU's mvhat die nette Option "destination first" ( -t) und xargskann die Ausführung seines Befehls überspringen, wenn überhaupt keine Eingabe erfolgt ( -r). Die Verwendung von -print0und stellt -0dementsprechend sicher, dass es nicht zu Unordnung kommt, wenn Dateinamen Leerzeichen und andere "lustige" Dinge enthalten.


2
Beachten Sie, dass -maxdepth, -print0, -0und -rsind auch GNU - Erweiterungen (obwohl einige von ihnen in anderen Implementierungen heute zu finden sind).
Stéphane Chazelas

1
Poige, viele unserer Benutzer verwenden kein Linux. Es ist hier üblich und sehr hilfreich, darauf hinzuweisen, welche Teile einer Antwort nicht portierbar sind. Die Site heißt " Unix & Linux" und viele Leute benutzen irgendeine Form von BSD oder Unix oder AIX oder macOS oder eingebetteten Systemen. Nimm es nicht persönlich.
Terdon

Ich nehme nichts persönlich. Ich habe jedoch eine Meinung, die Sie entfernt haben, wie ich jetzt sehe. Also wiederhole ich: Ich finde diese Kommentare "note it's GNU" ziemlich sinnlos. Sie sind es überhaupt nicht wert, hinzugefügt zu werden, zumal meine Antwort einen klaren Hinweis darauf enthält, dass sie auf der GNU-Version von basiert mv.
3.

5

Es ist wichtig zu wissen , dass es tatsächlich die Shell ist, die das foo*auf die Liste der übereinstimmenden Dateinamen erweitert, so dass sich selbst wenig mvtun kann.

Das Problem hierbei ist, dass, wenn ein Glob nicht übereinstimmt, einige Shells bash(und die meisten anderen Bourne-ähnlichen Shells, die in den späten 70er Jahren von der Bourne-Shell eingeführt wurden) das Muster wörtlich an den Befehl übergeben.

Also hier, wenn foo*keine Datei entspricht, anstatt den Befehl Abbruch (wie Pre-Bourne - Shells und mehrere modernen Schalen tun), so geht die Schale eine wortgetreue foo*Datei auf mv, so dass im Grunde fragt mvgenannt , die Datei zu bewegen foo*.

Diese Datei existiert nicht. Wenn dies der Fall wäre, hätte es tatsächlich mit dem Muster überein- gestimmt, sodass mvein Fehler gemeldet wird. Wäre das Muster foo[xy]stattdessen gewesen, mvhätte man versehentlich eine aufgerufene Datei foo[xy]anstelle der fooxund fooy-Dateien verschieben können.

Selbst in Shells, die dieses Problem nicht haben (vor Bourne, csh, tcsh, fish, zsh, bash -O failglob), wird immer noch ein Fehler angezeigt, mv foo* ~/bardiesmal jedoch von der Shell.

Wenn Sie möchten, dass es kein Fehler ist, wenn keine Datei gefunden wurde, foo*und in diesem Fall nichts verschieben möchten, sollten Sie zuerst die Liste der Dateien erstellen (auf eine Weise, die keinen Fehler verursacht, wie mit der nullglobOption von einige Muscheln), und dann nur aufrufen, mvist die Liste nicht leer.

Das wäre besser, als alle Fehler zu verbergen mv(als 2> /dev/nullwürde das Hinzufügen ), als ob mves aus irgendeinem anderen Grund fehlschlagen würde. Sie würden wahrscheinlich immer noch wissen wollen, warum.

in zsh

files=(foo*(N)) # where the N glob qualifier activates nullglob for that glob
(($#files == 0)) || mv -- $files ~/bar/

Oder verwenden Sie eine anonyme Funktion, um die Verwendung einer temporären Variablen zu vermeiden:

() { (($# == 0)) || mv -- "$@" ~/bar/; } foo*(N)

zshist eine dieser Shells, die den Bourne-Fehler nicht aufweisen und einen Fehler melden, ohne den Befehl auszuführen, wenn ein Glob nicht übereinstimmt (und die nullglobOption nicht aktiviert wurde). Hier können Sie also den zshFehler ausblenden und wiederherstellen stderr für mvso würden Sie immer noch die mvFehler sehen, wenn überhaupt, aber nicht den Fehler über die nicht passenden Globs:

(mv 2>&3 foo* ~/bar/) 3>&2 2>&-

Oder Sie könnten verwenden, zargswas auch Probleme vermeiden foo*würde, wenn der Glob zu man-Dateien erweitern würde.

autoload zargs # best in ~/.zshrc
zargs -r -- foo* -- mv -t ~/bar # here assuming GNU mv for its -t option

In ksh93:

files=(~(N)foo*)
((${#files[#]} == 0)) || mv -- "${files[@]}" ~/bar/

In der Bash:

bashEs gibt keine Syntax, die nur nullglobfür einen Glob aktiviert werden kann , und die failglobOption wird deaktiviert, nullglobsodass Sie Folgendes benötigen:

saved=$(shopt -p nullglob failglob) || true
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
eval "$saved"

oder setzen Sie die Optionen in einer Subshell, um sie zu speichern, bevor Sie sie speichern und anschließend wiederherstellen müssen.

(
  shopt -s nullglob
  shopt -u failglob
  files=(foo*)
  ((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
)

Im yash

(
  set -o nullglob
  files=(foo*)
  [ "${#files[@]}" -eq 0 ] || mv -- "${files[@]}" ~/bar/
)

Im fish

In der Fish Shell ist das Nullglob-Verhalten die Standardeinstellung für den setBefehl.

set files foo*
count $files > /dev/null; and mv -- $files ~/bar/

POSIXly

nullglobIn POSIX gibt es keine Option shund kein Array außer den Positionsparametern. Es gibt jedoch einen Trick, mit dem Sie feststellen können, ob ein Glob übereinstimmt oder nicht:

set -- foo[*] foo*
if [ "$1$2" != 'foo[*]foo*' ]; then
  shift
  mv -- "$@" ~/bar/
fi

Wenn Sie sowohl a foo[*]als auch foo*glob verwenden, können Sie zwischen dem Fall, in dem es keine übereinstimmende Datei gibt, und dem Fall, in dem es eine Datei gibt, die zufällig aufgerufen wird foo*(was ein set -- foo*nicht tun kann), unterscheiden.

Lesen Sie weiter:


3

Es ist wahrscheinlich nicht das Beste, aber Sie können den findBefehl verwenden, um zu überprüfen, ob der Ordner leer ist oder nicht:

find "foo*" -type f -exec mv {} ~/bar/ \;

1
Dies würde einen wörtlichen foo*Suchpfad geben find.
Kusalananda

3

Ich gehe davon aus, dass Sie bash verwenden, da dieser Fehler vom Verhalten von bash abhängt, um nicht übereinstimmende Globs für sich selbst zu erweitern. (Im Vergleich dazu löst zsh einen Fehler aus, wenn versucht wird, ein nicht übereinstimmendes Glob zu erweitern.)

Was ist also mit der folgenden Problemumgehung?

ls -d foo* >/dev/null 2>&1 && mv foo* ~/bar/

Dies wird leise die ignorieren , mvwenn ls -d foo*fehlschlägt, während immer noch Fehler Protokollierung , wenn ls foo*gelingt , aber die mvfehlschlägt. (Vorsicht, ls foo*kann aus anderen als den foo*nicht vorhandenen Gründen fehlschlagen , z. B. aufgrund unzureichender Rechte, eines Problems mit dem FS usw., sodass solche Bedingungen von dieser Lösung unbemerkt ignoriert werden.)


Ich interessiere mich hauptsächlich für bash, ja (und ich habe die Frage als getaggt bash). +1 für die Berücksichtigung der Tatsache, dass mvaus anderen Gründen als foo*nicht vorhanden fehlschlagen könnte .
Jonik

1
(In der Praxis werde ich das wahrscheinlich nicht verwenden, da es lsziemlich ausführlich ist und der Sinn des Befehls für zukünftige Leser zumindest ohne Kommentar nicht sehr klar ist.)
Jonik

ls -d foo*könnte auch aus anderen Gründen mit einem Exit-Status ungleich Null zurückkehren, z. B. nach a ln -s /nowhere foobar(zumindest bei einigen lsImplementierungen).
Stéphane Chazelas

3

Sie können zum Beispiel tun

mv 1>/dev/null 2>&1 foo* ~/bar/ oder mv foo* ~/bar/ 1&>2

Weitere Informationen finden Sie unter: http://mywiki.wooledge.org/BashFAQ/055


mv foo* ~/bar/ 1&>2Bringt den Befehl nicht zum Schweigen, sondern sendet das, was auf stdout gewesen wäre, um auch auf stderr zu sein.
Chris Down

und wenn du mingw unter windows verwendest (wie ich), ersetze es /dev/nulldurchNUL
Rian Sanderson

Wie funktioniert dieser Befehl? Was machen die Et-Zeichen? Was ist zu tun 1und zu 2bedeuten?
Aaron Franke

@AaronFranke 1 ist stdout und 2 ist standardmäßig stderr pipe, wenn ein Linux-Prozess erstellt wird. Erfahren Sie mehr über Pipe in Linux-Befehlen. Sie können hier beginnen - digitalocean.com/community/tutorials/…
Mithun B

2

Sie können (portabel) betrügen mit perl:

perl -e 'system "mv foo* ~/bar/" if glob "foo*"'

Bitte kommentieren Sie, bevor Sie abstimmen.
Joseph R.

2

Wenn Sie Perl verwenden möchten, können Sie genauso gut den ganzen Weg gehen:

#!/usr/bin/perl
use strict;
use warnings;
use File::Copy;

my $target = "$ENV{HOME}/bar/";

foreach my $file (<foo*>) {
    move $file, $target  or warn "Error moving $file to $target: $!\n";
}

oder als Einzeiler:

perl -MFile::Copy -E 'move $_, "$ENV{HOME}/bar/" or warn "$_: $!\n" for <foo*>'

(Einzelheiten zum moveBefehl finden Sie in der Dokumentation zu File :: Copy .)


-1

Stattdessen

mv foo* ~/bar/

du kannst tun

cp foo* ~/bar/
rm foo* 

Einfach, lesbar :)


6
Das Kopieren und Entfernen dauert länger als ein einfacher Zug. Es funktioniert auch nicht, wenn eine Datei größer als der verfügbare freie Speicherplatz ist.
Anthon

11
Das OP will eine leise Lösung. Weder cpnoch rmschweigen , wenn foo*nicht existiert.
Ron Rothman

-1
mv foo* ~/bar/ 2>/dev/null

Ob der obige Befehl erfolgreich ist oder nicht, können wir durch Beenden des vorherigen Befehls feststellen

command: echo $?

ob die Ausgabe von echo $? ist anders als 0 bedeutet, dass der Befehl nicht erfolgreich ist, wenn die Ausgabe 0 ist, bedeutet, dass der Befehl erfolgreich ist

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.