Wenn ich xargs
verwende, muss ich die ersetzende Zeichenfolge manchmal nicht explizit verwenden:
find . -name "*.txt" | xargs rm -rf
In anderen Fällen möchte ich die ersetzende Zeichenfolge angeben, um folgende Aufgaben auszuführen:
find . -name "*.txt" | xargs -I '{}' mv '{}' /foo/'{}'.bar
Der vorherige Befehl würde alle Textdateien unter dem aktuellen Verzeichnis in /foo
verschieben und die Erweiterung bar
an alle Dateien anhängen.
Wenn ich den Text nicht an den Ersetzungsstring anfügen wollte, sondern ihn so ändern, dass er zwischen dem Namen und der Erweiterung der Dateien Text einfügen kann, wie kann ich das tun? Angenommen, ich möchte das gleiche wie im vorherigen Beispiel tun, aber die Dateien sollten umbenannt/verschoben werden von <name>.txt
in /foo/<name>.bar.txt
(anstelle von /foo/<name>.txt.bar
).
UPDATE: Ich finde eine Lösung:
find . -name "*.txt" | xargs -I{} \
sh -c 'base=$(basename $1) ; name=${base%.*} ; ext=${base##*.} ; \
mv "$1" "foo/${name}.bar.${ext}"' -- {}
Aber ich frage mich, ob es eine kürzere/bessere Lösung gibt.
In solchen Fällen wäre eine while
-Schleife besser lesbar:
find . -name "*.txt" | while IFS= read -r pathname; do
base=$(basename "$pathname"); name=${base%.*}; ext=${base##*.}
mv "$pathname" "foo/${name}.bar.${ext}"
done
Beachten Sie, dass Sie möglicherweise Dateien mit demselben Namen in verschiedenen Unterverzeichnissen finden. Sind Sie in Ordnung, wenn Duplikate von mv
überschrieben werden?
Der folgende Befehl erstellt den Verschiebungsbefehl mit xargs und ersetzt das zweite Vorkommen von '.' mit '.bar.' führt dann die Befehle mit bash aus und arbeitet mit Mac OSX.
ls *.txt | xargs -I {} echo mv {} foo/{} | sed 's/\./.bar./2' | bash
Dies ist in einem Durchgang (getestet in GNU) möglich, sodass die temporären Variablenzuweisungen nicht verwendet werden
find . -name "*.txt" | xargs -I{} sh -c 'mv "$1" "foo/$(basename ${1%.*}).new.${1##*.}"' -- {}
Wenn Sie GNU Parallel http://www.gnu.org/software/parallel/ installiert haben, können Sie Folgendes tun:
find . -name "*.txt" | parallel 'ext="{/}" ; mv -- {} foo/{/.}.bar.${ext##*.}'
In den Introvideos zu GNU Parallel erfahren Sie mehr: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
Wenn Sie etwas anderes als bash/sh verwenden dürfen UND dies ist nur für ein schickes "mv" ... können Sie das ehrwürdige Skript "rename.pl" ausprobieren. Ich verwende es ständig unter Linux und cygwin unter Windows.
http://people.sc.fsu.edu/~jburkardt/pl_src/rename/rename.html
rename.pl 's/^(.*?)\.(.*)$/\1-new_stuff_here.\2/' list_of_files_or_glob
Sie können auch einen "-p" -Parameter zum Umbenennen von ".pl" verwenden, damit Sie wissen lassen, was es hätte tun sollen, ohne es tatsächlich zu tun.
Ich habe gerade folgendes in meiner c:/bin (cygwin/Windows-Umgebung) ausprobiert. Ich habe das "-p" verwendet, damit es ausspuckte, was es getan hätte. In diesem Beispiel werden Basis und Erweiterung nur geteilt und dazwischen eine Zeichenfolge eingefügt.
Perl c:/bin/rename.pl -p 's/^(.*?)\.(.*)$/\1-new_stuff_here.\2/' *.bat
rename "here.bat" => "here-new_stuff_here.bat"
rename "htmldecode.bat" => "htmldecode-new_stuff_here.bat"
rename "htmlencode.bat" => "htmlencode-new_stuff_here.bat"
rename "sdiff.bat" => "sdiff-new_stuff_here.bat"
rename "widvars.bat" => "widvars-new_stuff_here.bat"
die Dateien sollten umbenannt/verschoben werden von
<name>.txt
in/foo/<name>.bar.txt
Sie können das Dienstprogramm rename
verwenden, z.
rename s/\.txt$/\.txt\.bar/g *.txt
Hinweis: Die Subsitutionssyntax ist sed
oder vim
ähnlich.
Verschieben Sie dann die Dateien mit mv
in ein Zielverzeichnis:
mkdir /some/path
mv *.bar /some/path
Um Dateien in Unterverzeichnisse basierend auf einem Teil ihres Namens umzubenennen, prüfen Sie Folgendes:
-p
/--mkpath
/--make-dirs
Erstellen Sie nicht vorhandene Verzeichnisse im Zielpfad.
Testen:
$ touch {1..5}.txt
$ rename --dry-run "s/.txt$/.txt.bar/g" *.txt
'1.txt' would be renamed to '1.txt.bar'
'2.txt' would be renamed to '2.txt.bar'
'3.txt' would be renamed to '3.txt.bar'
'4.txt' would be renamed to '4.txt.bar'
'5.txt' would be renamed to '5.txt.bar'
Hinzu kommt, dass der Wikipedia-Artikel überraschend informativ ist
Shell-Trick
Eine andere Möglichkeit, einen ähnlichen Effekt zu erzielen, besteht darin, eine Shell als gestarteten Befehl zu verwenden und sich beispielsweise mit der Komplexität dieser Shell auseinanderzusetzen:
$ mkdir ~/backups
$ find /path -type f -name '*~' -print0 | xargs -0 bash -c 'for filename; do cp -a "$filename" ~/backups; done' bash
Inspiriert von einer Antwort von @justaname oben, wird dieser Befehl, der Perl-Einzeiler enthält, dies tun:
find ./ -name \*.txt | Perl -p -e 's/^(.*\/(.*)\.txt)$/mv $1 .\/foo\/$2.bar.txt/' | bash