Verschieben von Dateien, die einem Muster entsprechen, das das Verzeichnis erfasst

Verschieben von Dateien, die einem Muster entsprechen, das das Verzeichnis erfasst

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 itselfist in diesem Fall harmlos, Sie können es ignorieren. Ich meine, mvder 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 mvdies nicht erfolgreich ist.

Dies ist bereitssagte in einem Kommentar: Sie können mvstderr 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:

  1. fooselbst
  2. foo nur mit Präfix, wie bar-foo– Sie können sie abgleichen durch*?foo
  3. foo mit Präfix und Postfix, wie bar-foo-baz*?foo?*
  4. foo nur mit Postfix, wie foo-bazfoo?*

Zum Ausschließen foobenö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 nullglobShell-Option nicht gesetzt haben, muss jedes Muster mit etwas übereinstimmen, sonst wird es mvwö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, dotglobauch 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 --foowird 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-bazmv

Wenn überhaupt keine Übereinstimmung gefunden wird, mvdegenerieren meine Befehle mv foo/und werfen einen Fehler. Ihr ursprünglicher mvBefehl (mit nullglobunset) 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, dummyes 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 findjedenfalls

Dateien von Verzeichnissen zu unterscheiden ist eine Aufgabe für find. Was Sie mit gemacht haben, findist grundsätzlich die richtige Vorgehensweise. Was Sie (und was ich oben gemacht habe) mit und Shell-Globbing machen wollen, mvscheint … nun ja, höchstens „weniger richtig“. Dies ist Ihr Befehl:

find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;

Dadurch findwird 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 mvund findreich 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 findBefehl und/oder gegenüber Plain mvmit Glob(s):

  • eine einzelne Person mvkann mehrere Dateien zum Bearbeiten erhalten;
  • Wenn dennoch zu viele Dateien für eine Befehlszeile vorhanden sind, werden zusätzliche Prozesse findausgeführt .mv
  • mvwird überhaupt nicht ausgeführt , wenn keine Datei dem Muster entspricht ;
  • dank --einer Datei wie --foowird nicht als (unbekannte) Option für interpretiert mv;

verwandte Informationen