70

当我使用时,xargs有时我不需要显式使用替换字符串:

find . -name "*.txt" | xargs rm -rf

在其他情况下,我想指定替换字符串以执行以下操作:

find . -name "*.txt" | xargs -I '{}' mv '{}' /foo/'{}'.bar

上一个命令会将当前目录下的所有文本文件移动到/foo其中,并将扩展名附加bar到所有文件中。

如果我不想将一些文本附加到替换字符串,而是想修改该字符串,以便可以在文件的名称和扩展名之间插入一些文本,我该怎么做?例如,假设我想做与上一个示例相同的操作,但文件应该重命名/从<name>.txtto /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}"' -- {}

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

4

8 回答 8

47

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

ls *.txt | xargs -I {} echo mv {} foo/{} | sed 's/\./.bar./2' | bash
于 2014-02-21T08:10:11.483 回答
32

可以一次性完成(在 GNU 中测试),避免使用临时变量赋值

find . -name "*.txt" | xargs -I{} sh -c 'mv "$1" "foo/$(basename ${1%.*}).new.${1##*.}"' -- {}
于 2016-03-09T21:40:50.893 回答
20

在这种情况下,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吗?

于 2012-05-29T19:48:31.713 回答
12

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

于 2012-05-30T19:48:32.287 回答
2

如果你被允许使用 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”参数来重命名.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"
于 2012-05-29T18:31:15.710 回答
2

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

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

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

提示:替换语法类似于sedor 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'
于 2018-05-01T12:33:23.057 回答
1

再加上维基百科的文章令人惊讶地提供了丰富的信息

例如:

贝壳把戏

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

$ mkdir ~/backups
$ find /path -type f -name '*~' -print0 | xargs -0 bash -c 'for filename; do cp -a "$filename" ~/backups; done' bash
于 2018-07-23T16:50:52.217 回答
0

受上面@justaname 回答的启发,这个包含 Perl 单行代码的命令将执行此操作:

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

于 2016-06-14T08:54:15.723 回答