Halten:
$ ls
foo xyfooz.tex
$ find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;
$ ls ./foo*
xyfooz.tex
Ich versuche, die gleiche Befehlsfolge nur mit mv zu erreichen, finde aber nicht:
$ ls
foo xyfooz.tex
$ mv *foo* foo
mv: cannot stat '*foo*': No such file or directory
$ ls ./foo*
xyfooz.tex
Zusätzlich (Bearbeiten),
$ ls -F
foo/ xyfooz.tex*
$ mv -v *foo* foo
mv: cannot move 'foo' to a subdirectory of itself, 'foo/foo'
'xyfooz.tex' -> 'foo/xyfooz.tex'
Wie stelle ich sicher, dass die „stat“-Warnung nicht angezeigt wird? Mit anderen Worten, ich denke, ich würde das Muster verfeinern, um die Quelle auf Dateien und nicht auf Verzeichnisse zu beschränken?
GNU Bash, Version 4.3.48(1) (x86_64-pc-linux-gnu)
$ cat /etc/os-release
NAME="Linux Mint"
VERSION="18.3 (Sylvia)"
Antwort1
Mitmv
cannot move 'foo' to a subdirectory of itself
ist in diesem Fall harmlos, Sie können es ignorieren. Ich meine, mv
der Rest wird trotz der Warnung verschoben. Der Beendigungsstatus wird jedoch nicht sein 0
, dies kann ein großes Hindernis in Skripten sein, in denen Sie abbrechen oder eine Korrekturmaßnahme ergreifen möchten, wenn mv
dies nicht erfolgreich ist.
Dies ist bereitssagte in einem Kommentar: Sie können mv
stderr durch Umleiten mit stummschalten 2>/dev/null
; andere Warnungen oder Fehler werden jedoch ebenfalls umgeleitet.
Ich denke, Sie können das Muster nicht einfach „verfeinern, um die Quelle auf Dateien und nicht auf Verzeichnisse zu beschränken“. Sie können dennoch einen Ansatz wählen, der nicht wörtlich übereinstimmt foo
. Der folgende Ansatz hat nichts mit Dateien oder Verzeichnissen zu tun, sondern nur mit Namen, da Globs mit Namen arbeiten. Daher reicht er in manchen Fällen möglicherweise nicht aus.
Der *foo*
Glob passt zu vier disjunkten Objekttypen:
foo
selbst- foo nur mit Präfix, wie
bar-foo
– Sie können sie abgleichen durch*?foo
- foo mit Präfix und Postfix, wie
bar-foo-baz
–*?foo?*
- foo nur mit Postfix, wie
foo-baz
–foo?*
Zum Ausschließen foo
benötigen Sie nur die letzten drei (2-4), daher könnte der erste Ansatz wie folgt aussehen:
mv *?foo *?foo?* foo?* foo/
Sofern Sie die nullglob
Shell-Option nicht gesetzt haben, muss jedes Muster mit etwas übereinstimmen, sonst wird es mv
wörtlich weitergegeben. Das möchten Sie nicht. Die Lösung besteht darin, vorher den folgenden Befehl auszuführen:
shopt -s nullglob
Diese Shell-Option lässt jeden nicht übereinstimmenden Glob ins Nichts expandieren. Ich rate Ihnen, dotglob
auch diese Option zu erforschen.
Beachten Sie, dass *?foo*
dies mit (2-3) übereinstimmt. Gleiches *foo?*
gilt für (3-4). Denken Sie außerdem --
daran, anzugeben mv
, dass alle folgenden Argumente keine Optionen sind. Auf diese Weise --foo
wird eine Datei wie nicht als (unbekannte) Option interpretiert. Dies führt zu zwei besseren Befehlen:
mv -- *?foo* foo?* foo/
oder
mv -- *?foo *foo?* foo/
Es spielt keine Rolle, welches Sie wählen. Verwenden Sie einfach nicht ; es wird (eg) zweimal an mv *?foo* *foo?* foo/
übergeben (d. h. als zwei separate Argumente) und erzeugt somit einen Fehler, wenn das Tool versucht, dieses Objekt zum zweiten Mal zu verschieben.bar-foo-baz
mv
Wenn überhaupt keine Übereinstimmung gefunden wird, mv
degenerieren meine Befehle mv foo/
und werfen einen Fehler. Ihr ursprünglicher mv
Befehl (mit nullglob
unset) würde den Literalwert erhalten *foo*
und einen weiteren Fehler werfen.
Beispiel einer Konsolensitzung:
$ shopt -s nullglob
$ mkdir foo
$ touch bar-foo bar-foo-baz foo-baz xyfooz.tex ./--foo
$ mv -v -- *?foo* foo?* foo/
'bar-foo' -> 'foo/bar-foo'
'bar-foo-baz' -> 'foo/bar-foo-baz'
'--foo' -> 'foo/--foo'
'xyfooz.tex' -> 'foo/xyfooz.tex'
'foo-baz' -> 'foo/foo-baz'
$
Einfacher Hack
Sagen wir, dummy
es existiert nicht.
mv foo dummy
mv *foo* dummy/
mv dummy foo
Das Umbenennen eines Verzeichnisses sollte in Inode-basierten Dateisystemen sehr schnell gehen. Programme, die bereits Dateien darin foo/
geöffnet haben, sollten weder stören noch abstürzen, da es auf den Inode ankommt. Wenn jedoch ein Programm eine Datei (über ihren Pfad einschließlich foo/
) zwischen dem ersten und dem dritten öffnen muss mv
, schlägt dies fehl. Es können andere Nebenwirkungen auftreten (stellen Sie sich vor, ein Drittanbieterprogramm erstellt foo/
in der Zwischenzeit eine neue Datei), deshalb nenne ich diesen Ansatz einen Hack. Überlegen Sie es sich zweimal, bevor Sie ihn verwenden.
Mit find
jedenfalls
Dateien von Verzeichnissen zu unterscheiden ist eine Aufgabe für find
. Was Sie mit gemacht haben, find
ist grundsätzlich die richtige Vorgehensweise. Was Sie (und was ich oben gemacht habe) mit und Shell-Globbing machen wollen, mv
scheint … nun ja, höchstens „weniger richtig“. Dies ist Ihr Befehl:
find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;
Dadurch find
wird für jede übereinstimmende Datei ein separater Befehl ausgeführt mv
, die Leistung des gesamten Befehls ist schlecht. Aus diesem Grund kann es sinnvoll sein, auf einen einzelnen umzusteigen mv
. Wenn Sie so denken (und Ihr mv
und find
reich genug sind), sollten Sie diesen „sehr richtigen“ Weg in Betracht ziehen:
find . -maxdepth 1 -type f -name '*foo*' -exec mv -t foo/ -- {} +
Die Vorteile gegenüber Ihrem find
Befehl und/oder gegenüber Plain mv
mit Glob(s):
- eine einzelne Person
mv
kann mehrere Dateien zum Bearbeiten erhalten; - Wenn dennoch zu viele Dateien für eine Befehlszeile vorhanden sind, werden zusätzliche Prozesse
find
ausgeführt .mv
mv
wird überhaupt nicht ausgeführt , wenn keine Datei dem Muster entspricht ;- dank
--
einer Datei wie--foo
wird nicht als (unbekannte) Option für interpretiertmv
;