Fügen Sie allen Verzeichnissen in einem Verzeichnis ein Unterstrich-Präfix hinzu

Fügen Sie allen Verzeichnissen in einem Verzeichnis ein Unterstrich-Präfix hinzu

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 findutilsauf 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

_dir1und möchten die Verzeichnisse in , _dir2, _dir3? umbenennen.

Das find/ printf mvsieht 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 _$xbereits vorhanden ist, $xwird es dorthin verschoben.)

Wenn Sie Verzeichnisse einschließen möchten, deren Namen mit einem Punkt beginnen, verwenden Sie shopt -s dotglobvorher.

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 -execdirist es möglicherweise am einfachsten, Folgendes zu verwenden. Dabei wird der Befehl im Verzeichnis der Datei ausgeführt. Wir müssen -depthdie 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, -execwie 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 findDurchlaufen 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 foonach 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._foofoo_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 .zshrcoder in Ihr Skript ein), um diezmvFunktion, 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'

zmvlehnt eine Bewegung ab, wenn das Ziel bereits existiert.

verwandte Informationen