./Test kann nicht in ein Unterverzeichnis von './test/test' verschoben werden

1170
Egon Olieux

Beim Versuch, einige Dateien in Kleinbuchstaben zu konvertieren, fiel mir etwas auf, das mir seltsam erscheint.

Wenn Sie dies in Bash ausführen:

find . -iname 'test' | while IFS='\n' read item; do mv $item $(echo $item | tr [:upper:] [:lower:]); done 

Und die Quelle und das Ziel sind für mv unverändert, ich erhalte folgende Fehlermeldung:

mv: cannot move '.test' to a subdirectory of itself, './test/test' 

Woher kommt das ./test/test? Beim Echo, anstatt mv auszuführen, bekomme ich mv ./test ./test. Ich habe dies sowohl unter FreeBSD 10 als auch unter Debian 8.2.0 mit den gleichen Ergebnissen getestet. Was vermisse ich?

1
Okay, macht Sinn. Was würden Sie vorschlagen, wenn ich eine Reihe von Verzeichnissen umbenennen möchte? Kann dies ohne Fehler erfolgen, ohne explizit zu prüfen, ob sie gleich sind? Egon Olieux vor 8 Jahren 0
Ich verstehe, warum der Wechsel zu einem anderen Baum funktionieren würde, aber wie unterscheidet sich `./*` von `.`? `./*` erweitert sich auf jedes Element im Verzeichnis, während `.` das aktuelle Verzeichnis verwendet, aber am Ende bewegt es sich immer noch im selben Verzeichnis. Nehmen wir zum Beispiel das Verzeichnis "dir", das 5 Verzeichnisse enthält: "a", "b", "c", "d" und "e". Wenn ich alle Unterverzeichnisse von "dir" umbenennen möchte, "find." und `find ./*` liefert das gleiche Ergebnis. Egon Olieux vor 8 Jahren 0
Angenommen, das aktuelle Arbeitsverzeichnis ist "dir". Egon Olieux vor 8 Jahren 0
@FrankThomas + Egon: `-iname test` stimmt nicht mit` .test` überein; Aus dem zweiten Ergebnis (mit Echo) wird deutlich, dass es wirklich ". / test" ist und nur falsch zitiert wurde. "-ilname" ist genau dasselbe wie "-iname", mit Ausnahme von symbolischen Links, die hier nicht beteiligt sind. `./*` dehnt sich normalerweise auf alle Einträge in `.` aus, ausgenommen diejenigen, die mit` .` (Punkt) beginnen, obwohl einige Shells eine Option haben, um dies zu ändern, in `bash`` shopt -s dotglob`. OTOH `find .` betrachtet immer Punktnamen wie` ./. Profile` (mit Ausnahme von `.` und` ...`), obwohl hier kein solcher Name mit `-iname` übereinstimmt. dave_thompson_085 vor 8 Jahren 0

1 Antwort auf die Frage

2
Gabe

Ist der Name bereits in Kleinbuchstaben, wird er dem Befehl mv zweimal übergeben, wie Sie gesehen haben.

Wenn das letzte Argument für mv ein Verzeichnis ist, wird es als Zielverzeichnis behandelt, in das alle anderen benannten Entitäten verschoben werden sollen.

mv test testprüft zuerst das letzte Argument, ob es existiert und wenn ja, ob es ein Verzeichnis ist. In diesem Fall prüft mv, ob es sich um denselben Einhängepunkt handelt (wenn dies nicht der Fall ist, dh das Ziel ist ein anderes Gerät, muss die Datei kopiert und das Original entfernt werden). In diesem Fall ist es. So baut mv eine Folge von rename(2)syscalls, Anfügen alle Quellnamen wiederum in das Verzeichnis als Ziel angegeben: mv a b c d/erzeugen würde rename("a", "d/a"), rename("b", "d/b")und rename("c", "d/c")- so natürlich mv test testversuchen, zu nennen rename("test", "test/test");. Was natürlich ein Fehler ist, Sie können ein Verzeichnis nicht in sich selbst verschieben.

Um das Problem zu beheben, muss der mv-Befehl davon abhängig gemacht werden, dass der neue Name anders ist und noch nicht vorhanden ist (wenn Sie zwei verschiedene Dateien mit den Namen "Test" und "test" haben, möchten Sie wahrscheinlich nicht die zweite ersetzen, ohne zu fragen). Wenn ich das nicht getestet und auf einem Telefon new="$(echo "$old" | tr ...)"; [[ $new != $old && ! -e $new ]] && mv "$old" "$new"getippt habe, würde es ungefähr so ​​aussehen: (Achten Sie auf die Anführungszeichen, Sie möchten keine Leerzeichen in Namen, um Ihnen metaphorische Wedgies zu geben).