PS: Bitte berücksichtigen Sie auch Dateinamen mit ungeraden Zeichen (z. B. Leerzeichen)
Nathan JB
3
Sie müssen auch angeben , wie Sie den möglichen Fall , in dem handhaben möchten ./foo/bar/baz.pngund ./foo-bar-baz.pngbeide existieren. Ich nehme an, Sie möchten Letzteres nicht durch Ersteres ersetzen?
jw013
In meinem einen Fall ist es irrelevant, aber die Antwort sollte dies tatsächlich berücksichtigen, damit sich andere darauf verlassen können! Vielen Dank!
Nathan JB
Antworten:
7
Warnung: Ich habe die meisten dieser Befehle direkt in meinen Browser eingegeben. Vorbehalt Lektor.
Erläuterung: Das Muster **/*stimmt rekursiv mit allen Dateien in Unterverzeichnissen des aktuellen Verzeichnisses überein (es stimmt nicht mit Dateien im aktuellen Verzeichnis überein, diese müssen jedoch nicht umbenannt werden). Die ersten beiden Klammerpaare sind Gruppen, auf die als $1und $2im Ersatztext verwiesen werden kann . Das letzte Klammerpaar fügt das DGlob-Qualifikationsmerkmal hinzu, damit Punktdateien nicht weggelassen werden. -o -ibedeutet, die -iOption an zu übergeben, mvdamit Sie gefragt werden, ob eine vorhandene Datei überschrieben werden soll.
Mit nur POSIX-Tools:
find . -depth -exec sh -c '
for source; do
case $source in ./*/*)
target="$(printf %sz "${source#./}" | tr / -)";
mv -i -- "$source" "${target%z}";;
esac
done
' _ {} +
Erläuterung: In der caseAnweisung werden das aktuelle Verzeichnis und die Unterverzeichnisse der obersten Ebene des aktuellen Verzeichnisses weggelassen. targetenthält den Namen der Quelldatei ( $0), wobei der führende ./Strip entfernt und alle Schrägstriche durch Bindestriche ersetzt werden, sowie ein Finale z. Das Finale zist da, falls der Dateiname mit einer neuen Zeile endet: Andernfalls würde die Befehlsersetzung ihn entfernen.
Wenn Sie finddies nicht unterstützen -exec … +(OpenBSD, ich sehe Sie an):
find . -depth -exec sh -c '
case $0 in ./*/*)
target="$(printf %sz "${0#./}" | tr / -)";
mv -i -- "$0" "${target%z}";;
esac
' {} \;
Mit bash (oder ksh93) müssen Sie keinen externen Befehl aufrufen, um die Schrägstriche durch Bindestriche zu ersetzen. Sie können die Parametererweiterung ksh93 mit dem Konstrukt zum Ersetzen von Zeichenfolgen verwenden ${VAR//STRING/REPLACEMENT}:
find . -depth -exec bash -c '
for source; do
case $source in ./*/*)
source=${source#./}
target="${source//\//-}";
mv -i -- "$source" "$target";;
esac
done
' _ {} +
In den for sourceBeispielen fehlt die erste vorgestellte Datei / Verzeichnis ... Ich habe endlich herausgefunden, warum Sie (normalerweise) _als $0:) verwendet haben ... und danke für die tollen Antworten. Ich lerne so viel von ihnen.
Peter.O
Beachten Sie, dass das zmv-Beispiel nur einen Probelauf ausführt: Es verschiebt die Verschiebungsbefehle, führt sie jedoch nicht aus. Um diese Befehle tatsächlich auszuführen, entfernen Sie das n in -Qn.
Peter
5
Obwohl es hier bereits gute Antworten gibt, finde ich dies intuitiver in bash:
Wo aaaist Ihr vorhandenes Verzeichnis? Die darin enthaltenen Dateien werden in das aktuelle Verzeichnis verschoben (wobei nur leere Verzeichnisse in aaa verbleiben). Die beiden Aufrufe, trsowohl Verzeichnisse als auch Leerzeichen zu behandeln (ohne Logik für Schrägstriche - eine Übung für den Leser).
Ich halte dies für intuitiver und relativ einfach zu optimieren. Dh ich könnte die findParameter ändern , wenn ich nur PNG-Dateien finden möchte. Ich kann die trAnrufe ändern oder bei Bedarf weitere hinzufügen. Und ich kann echovorher hinzufügen, mvwenn ich visuell überprüfen möchte, ob es das Richtige tut (dann ohne das Extra echonur wiederholen, wenn die Dinge richtig aussehen:
Beachten Sie auch, dass ich find aaa... und nicht find .... oder find aaa/... verwende, da die beiden letzteren lustige Artefakte im endgültigen Dateinamen hinterlassen würden.
Vielen Dank für diesen einen Liner - das hat bei mir gut funktioniert, als ich es von außerhalb des Verzeichnisses gemacht habe (genau das haben Sie gesagt). Als ich versuchte, aus dem Verzeichnis heraus zu tun (in der Hoffnung, dass der Name des Stammverzeichnisses nicht hinzugefügt wird), "verschwanden" alle Dateien. Ich hatte sie in versteckte Dateien im selben Verzeichnis umgewandelt.
Hinweis: Dies funktioniert nicht für Dateinamen, die enthalten \n.
Dies verschiebt natürlich nur Typdateien f...
Die einzigen Namenskonflikte würden von Dateien stammen, die bereits im pwd vorhanden sind
Hier ist eine lsVersion .. Kommentare willkommen. Es funktioniert für verrückte Dateinamen wie diesen
"j1ळ\n\001\n/j2/j3/j4/\nh h\n", aber ich bin wirklich interessiert, irgendwelche Fallstricke zu kennen. Es ist eher eine Übung in "Kann der Bösartige es lstatsächlich (robust) tun?"
ls -AbQR1p * | # paths in "quoted" \escaped format
sed -n '/":$/,${/.[^/]$/p}' | # skip files in pwd, blank and dir/ lines
{ bwd="$PWD"; while read -n1; # save base dir strip leading "quote
read -r p; do # read path
printf -vs "${p%\"*}" # strip trailing quote"(:)
[[ $p == *: ]] && { # this is a directory
cd "$bwd/$s" # change into new directory
rnp="${s//\//-}"- # relative name prefix
} || mv "$s" "$bwd/$rnp$s"
done; }
Dies behandelt ungerade Dateinamen nicht sicher. Räume werden es (unter anderem) brechen.
jw013
Auf den ersten Blick ist dies eine saubere, lesbare Lösung, aber @ jw013 ist richtig, es wird nicht gut mit Leerzeichen umgehen. Würde cpes cp -v "$FILE" "$NEWFILE"helfen, die Leitung zu ändern ? (Außerdem sollten Sie nicht nur PNG-Dateien annehmen.)
Nathan JB
2
@ NathanJ.Brauer Das Hinzufügen von Anführungszeichen zur cpZeile ist unzureichend. Der gesamte Ansatz zum Parsen der findAusgabe mit einer forSchleife ist fehlerhaft. Die einzigen sicheren Verwendungsmöglichkeiten findsind mit -execoder, -print0falls verfügbar (weniger tragbar als -exec). Ich würde eine robustere Alternative vorschlagen, aber Ihre Frage ist derzeit nicht ausreichend spezifiziert.
jw013
-2
Sicher für Leerzeichen in Dateinamen:
#!/bin/bash
/bin/find $PWD -type f | while read FILE
do
_new="${FILE//\//-}"
_new="${_new:1}"
#echo $FILE
#echo $_new
/bin/mv "$FILE" "$_new"
done
Lief meine mit cpstatt aus mvoffensichtlichen Gründen, sollte aber das gleiche produzieren.
type find-> find is /usr/bin/findauf meiner Maschine. Die Verwendung PATHals Variablenname in einem Skript ist eine schreckliche Idee. Außerdem werden in Ihrem Skript Dateinamen mit Leerzeichen oder umgekehrten Schrägstrichen versehen, da Sie den readBefehl missbrauchen (suchen Sie auf dieser Site nach IFS read -r).
Gilles 'SO - hör auf böse zu sein'
find is hashed (/bin/find), relevant wie? Unterschiedliche Distributionen sind unterschiedlich?
Tim
Ich wusste, ich hätte mich von diesem Geierköder fernhalten sollen.
Tim
@Tim Sie können die Antwort jederzeit löschen, um Ihren Repräsentanten zurückzubekommen (und meiner auch b / c, es kostet Repräsentanten, um sie abzustimmen). Hartcodierte Pfade in Skripten scheinen darauf hinzudeuten, dass Ihnen der Punkt der PATHUmgebungsvariablen fehlt, den jedes Unix-System verwendet. Wenn Sie schreiben, /bin/findist Ihr Skript tatsächlich auf jedem System (Gilles, meins und wahrscheinlich den OPs) defekt, das es nicht gibt /bin/find(z. B. b / c, in dem es sich befindet /usr/bin/find). Der Sinn der PATHUmgebungsvariablen besteht darin, dies zu einem Nicht-Problem zu machen.
jw013
Ist übrigens $(pwd)schon in einer Variablen verfügbar : $PWD. Aber Sie müssen nicht einen absoluten Pfad hier verwenden: Wenn Ihr Skript aus, sagen wir aufgerufen wird, /home/timversucht es umbenennen /home/tim/some/pathzu /home-tim-some-path.
./foo/bar/baz.png
und./foo-bar-baz.png
beide existieren. Ich nehme an, Sie möchten Letzteres nicht durch Ersteres ersetzen?Antworten:
Warnung: Ich habe die meisten dieser Befehle direkt in meinen Browser eingegeben. Vorbehalt Lektor.
Mit zsh und zmv :
Erläuterung: Das Muster
**/*
stimmt rekursiv mit allen Dateien in Unterverzeichnissen des aktuellen Verzeichnisses überein (es stimmt nicht mit Dateien im aktuellen Verzeichnis überein, diese müssen jedoch nicht umbenannt werden). Die ersten beiden Klammerpaare sind Gruppen, auf die als$1
und$2
im Ersatztext verwiesen werden kann . Das letzte Klammerpaar fügt dasD
Glob-Qualifikationsmerkmal hinzu, damit Punktdateien nicht weggelassen werden.-o -i
bedeutet, die-i
Option an zu übergeben,mv
damit Sie gefragt werden, ob eine vorhandene Datei überschrieben werden soll.Mit nur POSIX-Tools:
Erläuterung: In der
case
Anweisung werden das aktuelle Verzeichnis und die Unterverzeichnisse der obersten Ebene des aktuellen Verzeichnisses weggelassen.target
enthält den Namen der Quelldatei ($0
), wobei der führende./
Strip entfernt und alle Schrägstriche durch Bindestriche ersetzt werden, sowie ein Finalez
. Das Finalez
ist da, falls der Dateiname mit einer neuen Zeile endet: Andernfalls würde die Befehlsersetzung ihn entfernen.Wenn Sie
find
dies nicht unterstützen-exec … +
(OpenBSD, ich sehe Sie an):Mit bash (oder ksh93) müssen Sie keinen externen Befehl aufrufen, um die Schrägstriche durch Bindestriche zu ersetzen. Sie können die Parametererweiterung ksh93 mit dem Konstrukt zum Ersetzen von Zeichenfolgen verwenden
${VAR//STRING/REPLACEMENT}
:quelle
for source
Beispielen fehlt die erste vorgestellte Datei / Verzeichnis ... Ich habe endlich herausgefunden, warum Sie (normalerweise)_
als$0
:) verwendet haben ... und danke für die tollen Antworten. Ich lerne so viel von ihnen.Obwohl es hier bereits gute Antworten gibt, finde ich dies intuitiver in
bash
:Wo
aaa
ist Ihr vorhandenes Verzeichnis? Die darin enthaltenen Dateien werden in das aktuelle Verzeichnis verschoben (wobei nur leere Verzeichnisse in aaa verbleiben). Die beiden Aufrufe,tr
sowohl Verzeichnisse als auch Leerzeichen zu behandeln (ohne Logik für Schrägstriche - eine Übung für den Leser).Ich halte dies für intuitiver und relativ einfach zu optimieren. Dh ich könnte die
find
Parameter ändern , wenn ich nur PNG-Dateien finden möchte. Ich kann dietr
Anrufe ändern oder bei Bedarf weitere hinzufügen. Und ich kannecho
vorher hinzufügen,mv
wenn ich visuell überprüfen möchte, ob es das Richtige tut (dann ohne das Extraecho
nur wiederholen, wenn die Dinge richtig aussehen:Beachten Sie auch, dass ich
find aaa
... und nichtfind .
... oderfind aaa/
... verwende, da die beiden letzteren lustige Artefakte im endgültigen Dateinamen hinterlassen würden.quelle
Hinweis: Dies funktioniert nicht für Dateinamen, die enthalten
\n
.Dies verschiebt natürlich nur Typdateien
f
...Die einzigen Namenskonflikte würden von Dateien stammen, die bereits im pwd vorhanden sind
Getestet mit dieser grundlegenden Teilmenge
Ergebend
quelle
Hier ist eine
ls
Version .. Kommentare willkommen. Es funktioniert für verrückte Dateinamen wie diesen"j1ळ\n\001\n/j2/j3/j4/\nh h\n"
, aber ich bin wirklich interessiert, irgendwelche Fallstricke zu kennen. Es ist eher eine Übung in "Kann der Bösartige esls
tatsächlich (robust) tun?"quelle
Leiten Sie einfach den Dateinamen + den Pfad zum
tr
Befehl weiter.tr <replace> <with>
quelle
cp
escp -v "$FILE" "$NEWFILE"
helfen, die Leitung zu ändern ? (Außerdem sollten Sie nicht nur PNG-Dateien annehmen.)cp
Zeile ist unzureichend. Der gesamte Ansatz zum Parsen derfind
Ausgabe mit einerfor
Schleife ist fehlerhaft. Die einzigen sicheren Verwendungsmöglichkeitenfind
sind mit-exec
oder,-print0
falls verfügbar (weniger tragbar als-exec
). Ich würde eine robustere Alternative vorschlagen, aber Ihre Frage ist derzeit nicht ausreichend spezifiziert.Sicher für Leerzeichen in Dateinamen:
Lief meine mit
cp
statt ausmv
offensichtlichen Gründen, sollte aber das gleiche produzieren.quelle
type find
->find is /usr/bin/find
auf meiner Maschine. Die VerwendungPATH
als Variablenname in einem Skript ist eine schreckliche Idee. Außerdem werden in Ihrem Skript Dateinamen mit Leerzeichen oder umgekehrten Schrägstrichen versehen, da Sie denread
Befehl missbrauchen (suchen Sie auf dieser Site nachIFS read -r
).find is hashed (/bin/find)
, relevant wie? Unterschiedliche Distributionen sind unterschiedlich?PATH
Umgebungsvariablen fehlt, den jedes Unix-System verwendet. Wenn Sie schreiben,/bin/find
ist Ihr Skript tatsächlich auf jedem System (Gilles, meins und wahrscheinlich den OPs) defekt, das es nicht gibt/bin/find
(z. B. b / c, in dem es sich befindet/usr/bin/find
). Der Sinn derPATH
Umgebungsvariablen besteht darin, dies zu einem Nicht-Problem zu machen.$(pwd)
schon in einer Variablen verfügbar :$PWD
. Aber Sie müssen nicht einen absoluten Pfad hier verwenden: Wenn Ihr Skript aus, sagen wir aufgerufen wird,/home/tim
versucht es umbenennen/home/tim/some/path
zu/home-tim-some-path
.