修改xargs中的替换字符串

时间:2012-05-29 16:41:51

标签: bash 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)。

更新:我设法找到解决方案:

find . -name "*.txt" | xargs -I{} \
    sh -c 'base=$(basename $1) ; name=${base%.*} ; ext=${base##*.} ; \
           mv "$1" "foo/${name}.bar.${ext}"' -- {}

但我想知道是否有更短/更好的解决方案。

8 个答案:

答案 0 :(得分:31)

以下命令使用xargs构造move命令,替换第二次出现的'。'使用'.bar。',然后使用bash执行命令,在mac OSX上工作。

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

答案 1 :(得分:18)

可以在一次通过(在GNU中测试)中执行此操作,避免使用临时变量赋值

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

答案 2 :(得分:17)

在这种情况下,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覆盖重复项?

答案 3 :(得分:11)

如果你安装了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

答案 4 :(得分:2)

如果你被允许使用bash / sh之外的其他东西,这只是为了一个花哨的“mv”...你可以尝试古老的“rename.pl”脚本。我一直在Linux和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”参数重命名.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"

答案 5 :(得分:1)

  

应将文件重命名/从<name>.txt移至/foo/<name>.bar.txt

您可以使用rename实用程序,例如:

rename s/\.txt$/\.txt\.bar/g *.txt

提示:替代语法类似于sedvim

然后使用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'

答案 6 :(得分:0)

受上面@justaname的回答启发,这个包含Perl one-liner的命令将会这样做:

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

答案 7 :(得分:0)

补充说明,wikipedia article内容丰富

for example:

  

Shell技巧

     

获得类似效果的另一种方法是使用shell作为启动命令,并处理该shell中的复杂性,例如:

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