Dateien verschieben, die einem Muster entsprechen, das das Verzeichnis abfängt

528
Erwann

Erwägen:

$ ls foo xyfooz.tex $ find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \; $ ls ./foo* xyfooz.tex 

Ich versuche die gleiche Reihenfolge von Anweisungen nur mit mv zu erreichen, nicht zu finden:

$ 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' 

So stellen Sie sicher, dass die Warnung "stat" 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, 4.3.48 (1) -release (x86_64-pc-linux-gnu)

$ cat /etc/os-release NAME="Linux Mint" VERSION="18.3 (Sylvia)" 
0
Sie könnten "mv" zum Schweigen bringen, indem Sie stderr mit "2> / dev / null" umleiten. Der Befehl `find` passt jedoch besser zu dem, was Sie versuchen. dsstorefile1 vor 6 Jahren 0
Es ist nicht klar, was los ist, aber die Shell-Erweiterung kann nicht auf Dateien beschränkt sein: Sie müssen `find` oder` for f in * foo * verwenden. do [-f "$ f"] && mv "$ f" foo; fertig ". Aktualisieren Sie Ihre Frage mit den Ergebnissen, wenn Sie beide Tests jeweils mit 'ls -F' und 'mv -v' sowohl im `find ... -exec` als auch im Stand-Alone-Befehl wiederholen. Ich verwende Ubuntu 16.04.04 mit `GNU bash, Version 4.3.48 (1) -release (x86_64-pc-linux-gnu)`. Wenn "foo" ein Verzeichnis ist und "xyfooz.tex" eine Datei, gibt mein eigenständiges "mv" "mv:" kann "foo" nicht in ein Unterverzeichnis von "foo / foo" verschieben, was erwartungsgemäß ist. Welches Betriebssystem verwendest du? AFH vor 6 Jahren 0
Ich habe entsprechend Ihrem Vorschlag Änderungen vorgenommen, aber sie zeigen uns nichts Neues. Ich verwende dieselbe Bash-Version, die schon in der Frage stand. Ich hätte gehofft, es gäbe einen Regex-Weg, um die Quelle auf den Typ der Datei zu beschränken. Erwann vor 6 Jahren 0

1 Antwort auf die Frage

1
Kamil Maciorowski

Mit mv

cannot move 'foo' to a subdirectory of itselfist in diesem Fall harmlos, Sie können es ignorieren. Ich meine, ich mvwerde den Rest trotz der Warnung verschieben. Der Exit-Status ist jedoch nicht der Fall 0. Dies kann ein großes Hindernis in Skripts sein, bei denen Sie abbrechen möchten oder eine Korrekturaktion durchführen möchten, falls mvdies nicht erfolgreich ist.

Dies ist bereits in einem Kommentar gesagt worden : Sie können zum Schweigen bringen, mvindem Sie stderr mit umleiten 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". Trotzdem können Sie einen Ansatz wählen, der nicht mit dem Literal übereinstimmt foo. Der folgende Ansatz hat nichts mit Dateien oder Verzeichnissen zu tun. Nur mit Namen, weil Globs sich mit Namen beschäftigen; In manchen Fällen reicht es möglicherweise nicht aus.

Der *foo*Glob entspricht vier disjunktiven Objekten:

  1. foo selbst
  2. foo nur mit Präfix, wie bar-foo- du kannst sie nach anpassen*?foo
  3. Foo mit Präfix und Postfix, wie bar-foo-baz-*?foo?*
  4. foo nur mit postfix, wie foo-baz-foo?*

Um dies auszuschließen foo, brauchen Sie nur die letzten drei (2-4), so dass der erste Ansatz sein kann:

mv *?foo *?foo?* foo?* foo/ 

Wenn Sie nicht die nullglobShell-Option festgelegt haben, muss jedes Muster mit einem bestimmten Element übereinstimmen. Andernfalls wird es mvwörtlich übergeben. Du willst das nicht. Die Lösung besteht darin, den folgenden Befehl vorher auszuführen:

shopt -s nullglob 

Mit dieser Shell-Option wird ein nicht übereinstimmender Glob zu nichts erweitert. Ich empfehle Ihnen, auch nach dotglobMöglichkeiten zu suchen.

Beachten Sie, dass die *?foo*Übereinstimmungen (2-3) übereinstimmen. Gleiche *foo?*Übereinstimmung (3-4). Beachten Sie außerdem --, mvdass alle folgenden Argumente keine Optionen sind. Auf diese Weise wird eine Datei wie --foodiese nicht als (unbekannte) Option interpretiert. Dies führt zu zwei besseren Befehlen:

mv -- *?foo* foo?* foo/ 

oder

mv -- *?foo *foo?* foo/ 

Es ist egal, für welchen Sie sich entscheiden. Einfach nicht verwenden mv *?foo* *foo?* foo/; es wird (z) passieren bar-foo-bazzu mvzweimal (dh als zwei getrennte Argumente), wodurch ein Fehler erzeugt, wenn das Werkzeug, um dieses Objekt zum zweiten Mal zu bewegen versucht.

Wenn überhaupt keine Übereinstimmung gefunden mvwird, degenerieren sich meine Befehle mv foo/und werfen einen Fehler. Ihr ursprünglicher mvBefehl (mit nullglobunset) würde das Literal abrufen *foo*und einen anderen Fehler auslösen.


Beispielkonsolensitzung:

$ 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 dummymal, existiert nicht.

mv foo dummy mv *foo* dummy/ mv dummy foo 

Das Umbenennen eines Verzeichnisses sollte in Inode-basierten Dateisystemen sehr schnell sein. Programme, deren Dateien bereits von innen foo/geöffnet sind, sollten weder stören noch zerbrechen, da der Inode von Bedeutung ist. Wenn jedoch ein Programm eine Datei (einschließlich des Pfads foo/) zwischen der ersten und der dritten Datei öffnen muss, schlägt mvdies fehl. Andere Nebenwirkungen können auftreten (stellen Sie sich ein Drittanbieter-Programm vor, das foo/in der Zwischenzeit neu erstellt wurde). Deshalb bezeichne ich diesen Ansatz als Hack. Überlegen Sie zweimal, bevor Sie es verwenden.


Mit findsowieso

Dateien von Verzeichnissen zu trennen, ist eine Aufgabe für find. Was Sie damit gemacht haben, findist im Grunde der richtige Weg. Was Sie (und was ich oben gemacht habe) mit mvShell-Globbing machen möchte, scheint… naja, "weniger richtig". Dies ist dein Befehl:

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

Dadurch findwird mvfür jede übereinstimmende Datei ein separater Befehl ausgeführt, der gesamte Befehl wird schlecht funktionieren. Aus diesem Grund möchte man vielleicht zu einer Single wechseln mv. Wenn dies Ihre Linie ist (und Ihre mvund findsind reich genug), 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 findKommando und / oder einfach mvmit glob (s):

  • Ein einzelner mvBenutzer kann mehrere Dateien zur Bearbeitung erhalten.
  • Wenn jedoch zu viele Dateien für eine Befehlszeile vorhanden sind, findwerden zusätzliche mvProzesse ausgeführt.
  • Wenn keine Datei mit dem Muster übereinstimmt, mvwird sie überhaupt nicht ausgeführt.
  • dank --einer Datei wie --foowird nicht als (unbekannte) Option interpretiert mv;
Umfassend, wollte nur Danke sagen, werde es untersuchen. Erwann vor 5 Jahren 0