web-dev-qa-db-ja.com

xargsの置換文字列の変更

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}"' -- {}

しかし、より短い/より良い解決策があるのだろうか。

45
betabandido

このような場合、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によって重複が上書きされても大丈夫ですか?

17
glenn jackman

次のコマンドは、xargsを使用してmoveコマンドを作成し、2番目の「。」を置き換えます。 「.bar。」を使用して、bashでコマンドを実行し、mac OSXで動作します。

ls *.txt | xargs -I {} echo mv {} foo/{} | sed 's/\./.bar./2' | bash
33
fairidox

一時的な変数の割り当ての使用を避けて、1つのパス(GNUでテスト済み)でこれを行うことができます。

find . -name "*.txt" | xargs -I{} sh -c 'mv "$1" "foo/$(basename ${1%.*}).new.${1##*.}"' -- {}
20
Santrix

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

11
Ole Tange

ファイルの名前を<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'
2
kenorb

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"
2

さらに、 wikipediaの記事 は驚くほど有益です

例:

シェルトリック

同様の効果を実現する別の方法は、起動されたコマンドとしてシェルを使用し、そのシェルの複雑さに対処することです。次に例を示します。

$ mkdir ~/backups
$ find /path -type f -name '*~' -print0 | xargs -0 bash -c 'for filename; do cp -a "$filename" ~/backups; done' bash
0
Mike Graf

上記の@justanameの回答に触発され、Perlのワンライナーを組み込んだこのコマンドはそれを実行します。

find ./ -name \*.txt | Perl -p -e 's/^(.*\/(.*)\.txt)$/mv $1 .\/foo\/$2.bar.txt/' | bash

0
Vasiliy