xargs
を使用している場合、置換文字列を明示的に使用する必要がない場合があります。
find . -name "*.txt" | xargs rm -rf
他の場合には、次のようなことをするために置換文字列を指定したいと思います。
find . -name "*.txt" | xargs -I '{}' mv '{}' /foo/'{}'.bar
前のコマンドは、現在のディレクトリの下のすべてのテキストファイルを/foo
に移動し、拡張子bar
をすべてのファイルに追加します。
置換文字列にテキストを追加する代わりに、ファイルの名前と拡張子の間にテキストを挿入できるようにその文字列を変更したい場合、どうすればよいですか?たとえば、前の例と同じようにしたいが、ファイルの名前を<name>.txt
から/foo/<name>.bar.txt
の代わりに/foo/<name>.txt.bar
に変更/移動する必要があるとします。
[〜#〜] update [〜#〜]:私は解決策を見つけることができました:
find . -name "*.txt" | xargs -I{} \
sh -c 'base=$(basename $1) ; name=${base%.*} ; ext=${base##*.} ; \
mv "$1" "foo/${name}.bar.${ext}"' -- {}
しかし、より短い/より良い解決策があるのだろうか。
このような場合、while
ループはより読みやすくなります。
find . -name "*.txt" | while IFS= read -r pathname; do
base=$(basename "$pathname"); name=${base%.*}; ext=${base##*.}
mv "$pathname" "foo/${name}.bar.${ext}"
done
異なるサブディレクトリに同じ名前のファイルが見つかる場合があることに注意してください。 mv
によって重複が上書きされても大丈夫ですか?
次のコマンドは、xargsを使用してmoveコマンドを作成し、2番目の「。」を置き換えます。 「.bar。」を使用して、bashでコマンドを実行し、mac OSXで動作します。
ls *.txt | xargs -I {} echo mv {} foo/{} | sed 's/\./.bar./2' | bash
一時的な変数の割り当ての使用を避けて、1つのパス(GNUでテスト済み)でこれを行うことができます。
find . -name "*.txt" | xargs -I{} sh -c 'mv "$1" "foo/$(basename ${1%.*}).new.${1##*.}"' -- {}
GNU Parallel http://www.gnu.org/software/parallel/ がインストールされている場合、これを行うことができます。
find . -name "*.txt" | parallel 'ext="{/}" ; mv -- {} foo/{/.}.bar.${ext##*.}'
GNU Parallelで詳細をご覧ください: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
ファイルの名前を
<name>.txt
から/foo/<name>.bar.txt
に変更または移動する必要があります
rename
ユーティリティを使用できます。例:
rename s/\.txt$/\.txt\.bar/g *.txt
ヒント:置換構文はsed
またはvim
に似ています。
次に、mv
を使用してファイルをターゲットディレクトリに移動します。
mkdir /some/path
mv *.bar /some/path
名前の一部に基づいてファイルの名前をサブディレクトリに変更するには、以下を確認します。
-p
/--mkpath
/--make-dirs
ターゲットパスに存在しないディレクトリを作成します。
テスト:
$ 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'
Bash/sh以外のものを使用することが許可されていて、これが単なる「mv」の場合...由緒ある「rename.pl」スクリプトを試すことができます。私はLinux上でそれを、Windows上でcygwinを常に使用しています。
http://people.sc.fsu.edu/~jburkardt/pl_src/rename/rename.html
rename.pl 's/^(.*?)\.(.*)$/\1-new_stuff_here.\2/' list_of_files_or_glob
また、 "-p"パラメーターを使用してrename.plを使用し、実際に実行せずに、どのような処理が完了したかを通知することもできます。
私はc:/ bin(cygwin/windows環境)で次のことを試しました。 「-p」を使用したので、実行されるはずの処理が吐き出されました。この例では、ベースとエクステンションを分割し、それらの間に文字列を追加します。
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"
さらに、 wikipediaの記事 は驚くほど有益です
シェルトリック
同様の効果を実現する別の方法は、起動されたコマンドとしてシェルを使用し、そのシェルの複雑さに対処することです。次に例を示します。
$ mkdir ~/backups
$ find /path -type f -name '*~' -print0 | xargs -0 bash -c 'for filename; do cp -a "$filename" ~/backups; done' bash
上記の@justanameの回答に触発され、Perlのワンライナーを組み込んだこのコマンドはそれを実行します。
find ./ -name \*.txt | Perl -p -e 's/^(.*\/(.*)\.txt)$/mv $1 .\/foo\/$2.bar.txt/' | bash