Wie kann ich ein Verzeichnis im folgenden Format reduzieren?
Vor: ./aaa/bbb/ccc.png
Nach: ./aaa-bbb-ccc.png
./foo/bar/baz.png
und ./foo-bar-baz.png
beide existieren. Ich nehme an, Sie möchten Letzteres nicht durch Ersteres ersetzen?
Wie kann ich ein Verzeichnis im folgenden Format reduzieren?
Vor: ./aaa/bbb/ccc.png
Nach: ./aaa-bbb-ccc.png
./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 :
zmv -o -i -Qn '(**/)(*)(D)' '${1//\//-}$2'
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 das D
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:
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 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 Finale z
. Das Finale z
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):
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
' _ {} +
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
:
find aaa -type f -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); mv "{}" "$new"' \;
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 die tr
Anrufe ändern oder bei Bedarf weitere hinzufügen. Und ich kann echo
vorher hinzufügen, mv
wenn ich visuell überprüfen möchte, ob es das Richtige tut (dann ohne das Extra echo
nur wiederholen, wenn die Dinge richtig aussehen:
find aaa -name \*.png -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); echo mv "{}" "$new"' \;
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.
find . -mindepth 2 -type f -name '*' |
perl -l000ne 'print $_; s/\//-/g; s/^\.-/.\// and print' |
xargs -0n2 mv
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
rm -fr junk
rm -f junk*hello*
mkdir -p junk/junkier/junkiest
touch 'hello hello'
touch 'junk/hello hello'
touch 'junk/junkier/hello hello'
touch 'junk/junkier/junkiest/hello hello'
Ergebend
./hello hello
./junk-hello hello
./junk-junkier-hello hello
./junk-junkier-junkiest-hello hello
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 es ls
tatsä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; }
Leiten Sie einfach den Dateinamen + den Pfad zum tr
Befehl weiter.tr <replace> <with>
for FILE in `find aaa -name "*.png"`
do
NEWFILE=`echo $FILE | tr '/' '-'`
cp -v $FILE $NEWFILE
done
cp
es cp -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 der find
Ausgabe mit einer for
Schleife ist fehlerhaft. Die einzigen sicheren Verwendungsmöglichkeiten find
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:
#!/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 cp
statt aus mv
offensichtlichen Gründen, sollte aber das gleiche produzieren.
type find
-> find is /usr/bin/find
auf meiner Maschine. Die Verwendung PATH
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 den read
Befehl missbrauchen (suchen Sie auf dieser Site nach IFS 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 der PATH
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
.