Ich habe mehrere Verzeichnisse innerhalb eines Verzeichnisses, die ich umbenennen muss, um am Anfang des Namens einen Unterstrich „_“ einzufügen.
Als Test habe ich ein übergeordnetes Verzeichnis erstellt, dessen Inhalt drei Verzeichnisse umfasst:
dir1, dir2 and dir3; three files: file1, file2, file3
und drei Dateien mit Punktpräfix:
.file1, .file2, .file3
Bisher habe ich versucht, Folgendes zu verwenden gfind
, nachdem ich es findutils
auf dem Mac installiert hatte:
gfind . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh
Dies gibt Folgendes zurück:
mv: rename ./. to ./_.: Invalid argument
Also habe ich versucht, „rename“ mit „find“ zu verwenden:
find . -type d -exec rename 's/^/_/' {} \;
Dies gibt Folgendes zurück:
Can't rename '.' to '_.': Invalid argument
Can't rename './dir1' to '_./dir1': No such file or directory
Can't rename './dir2' to '_./dir2': No such file or directory
Can't rename './dir3' to '_./dir3': No such file or directory
Hat jemand zufällig eine praktikable Lösung oder weiß jemand, wo ich es vermasseln könnte?
Antwort1
Wenn ich es richtig verstanden habe, haben Sie Dateien und Verzeichnisse wie diese:
$ mkdir dir{1,2,3} ; touch file{1,2,3} .file{1,2,3}
$ ls -A
.file1 .file2 .file3 dir1 dir2 dir3 file1 file2 file3
_dir1
und möchten die Verzeichnisse in , _dir2
, _dir3
? umbenennen.
Das find
/ printf mv
sieht machbar aus. Mal sehen, was es ausgibt:
$ find . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n"
mv "./." "./_."
mv "./dir2" "./_dir2"
mv "./dir3" "./_dir3"
mv "./dir1" "./_dir1"
Der erste Befehl erzeugt einen Fehler, da der „Punkt“ speziell ist und nicht umbenannt werden kann. Bei den anderen sollte es aber funktionieren und die Verzeichnisse sollten wie gewünscht umbenannt werden:
$ find . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh
mv: cannot move ‘./.’ to ‘./_.’: Device or resource busy
$ ls -d _dir*
_dir1 _dir2 _dir3
Das Weiterleiten von Befehlen an eine Shell ist jedoch etwas hässlich und wenn die Dateinamen seltsam genug sind, können die Ergebnisse überraschend sein (z. B. wenn sie $
Zeichen enthalten, die eine Variablen- oder Befehlsersetzung in der Shell auslösen).
Wenn sich alle Dateien auf einer Ebene befinden, sollte dies funktionieren:
for x in * ; do [ -d "$x" ] && mv "$x" "_$x" ; done
(Wenn es jedoch _$x
bereits vorhanden ist, $x
wird es dorthin verschoben.)
Wenn Sie Verzeichnisse einschließen möchten, deren Namen mit einem Punkt beginnen, verwenden Sie shopt -s dotglob
vorher.
Das hier ist auch nah dran:
find . -type d -exec rename 's/^/_/' {} \;
Da jedoch die Pfade mit beginnen find
, müssen wir dies berücksichtigen. Dies sollte nur auf einer Ebene funktionieren (ändert die führende in ):rename
./
./
./_
find . -type d -exec rename 's,^./,./_,' {} \;
Um die Verzeichnisse auf allen Ebenen abzurufen, find -execdir
ist es möglicherweise am einfachsten, Folgendes zu verwenden. Dabei wird der Befehl im Verzeichnis der Datei ausgeführt. Wir müssen -depth
die Umbenennungen in der richtigen Reihenfolge vornehmen.
find . -depth -type d -execdir rename 's,^./,./_,' {} \;
Fügen Sie möglicherweise ! -name .
auch hinzu. zB
$ mkdir foo foo/dir1 foo/dir2
$ find . -depth \! -name . -type d -execdir rename 's,^./,./_,' {} \;
$ ls _foo
_dir1 _dir2
Antwort2
Das Problem bei Ihrem ersten Versuch besteht darin, dass Sie versuchen, .
sich selbst (das aktuelle Verzeichnis) und alle Unterverzeichnisse umzubenennen. Überspringen Sie es.
gfind . -mindepth 1 -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh
Aber tun Sie das eigentlich nicht. Leiten Sie niemals Dinge in weiter sh
.Das funktioniert nichtes sei denn, Ihre Dateinamen enthalten zufällig keine Shell-Sonderzeichen. Wenn Sie eine Datei mit dem Namen `rm -r ~`
(ein vollkommen gültiger, wenn auch ungewöhnlicher Dateiname) haben, wechselt pop zu Ihrem Home-Verzeichnis. Rufen Sie eine Shell auf, -exec
wie ich unten zeige.
Ihr zweiter Versuch funktioniert nicht, weil Sie das _
am Anfang des vollständigen Pfads einfügen, anstatt es am Anfang des Basisnamens hinzuzufügen, also nach dem letzten /
.
find . -mindepth 1 -type d -exec rename 's![^/]+$!_$&!' {} \;
Keines davon funktioniert wirklich, wenn Sie verschachtelte Verzeichnisse haben, weil sie die Verzeichnisse beim find
Durchlaufen umbenennen. Wenn Sie dies tun, müssen Sie mit der Option auf den Inhalt eines Verzeichnisses reagieren, bevor Sie auf das Verzeichnis selbst reagieren -depth
.
find . -mindepth 1 -depth -type d -exec rename 's![^/]+$!_$&!' {} \;
Portabler ist es, eine Shell aufzurufen und auszuführen mv
. Dies funktioniert auf jedem POSIX-System.
find . -depth -type d -name . -o -exec sh -c '
for d do mv "$d" "${d%/*}/_${d##*/}"; done
' sh {} +
Wenn keine verschachtelten Unterverzeichnisse vorhanden sind, können Sie eine einfache Shell-Schleife verwenden. Wenn Sie Verzeichnisse, deren Namen mit einem Punkt beginnen, nicht umbenennen möchten, verwenden Sie einfach das */
Platzhalterzeichen, das nur auf Verzeichnisse und symbolische Links zu Verzeichnissen zutrifft.
for d in */; do
if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi # robustness in case there are no subdirectories
if [ -L "$d" ]; then continue; fi # optional: skip symbolic links to directories
mv -- "$d" "_${d%/}"
done
Wenn Sie alle Verzeichnisse umbenennen möchten, einschließlich derjenigen, deren Name mit einem Punkt beginnt, schließen Sie .*
auch das Platzhalterzeichen ein.
for d in .*/ */; do
if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi
if [ -L "$d" ]; then continue; fi
mv -- "$d" "_${d%/}"
done
Beachten Sie, dass beim Verschieben foo
nach das alte Verzeichnis nach verschoben wird _foo
, wenn bereits ein Verzeichnis mit dem Namen vorhanden ist . Um dies zu verhindern, fügen Sie einen expliziten Test hinzu._foo
foo
_foo/foo
Alternativ können Sie auch zsh verwenden. Es ist auf macOS vorinstalliert. Führen Sie es zuerst aus autoload -U zmv
(fügen Sie es in Ihr .zshrc
oder in Ihr Skript ein), um diezmv
Funktion, dann ist die nichtrekursive Version (einschließlich Punktdateien, ausschließlich symbolischer Links) (/D)
eine Reihe vonGlob-Qualifikation):
zmv -Q '*(/D)' '_$f'
oder die rekursive Version (mitModifikatorenum den Verzeichnisnamen und den Basisnamen zu extrahieren):
zmv -Q '**/*(/D)' '$f:h/_$f:t'
zmv
lehnt eine Bewegung ab, wenn das Ziel bereits existiert.