bash 修改 xargs 中的替换字符串

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10803296/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 22:10:09  来源:igfitidea点击:

Modifying replace string in xargs

bashxargs

提问by betabandido

When I am using xargssometimes I do not need to explicitly use the replacing string:

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

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

In other cases, I want to specify the replacing string in order to do things like:

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

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

The previous command would move all the text files under the current directory into /fooand it will append the extension barto all the files.

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

If instead of appending some text to the replace string, I wanted to modify that string such that I could insert some text between the name and extension of the files, how could I do that? For instance, let's say I want to do the same as in the previous example, but the files should be renamed/moved from <name>.txtto /foo/<name>.bar.txt(instead of /foo/<name>.txt.bar).

如果我不想在替换字符串中附加一些文本,而是想修改该字符串,以便可以在文件的名称和扩展名之间插入一些文本,我该怎么做?例如,假设我想做与上一个示例相同的操作,但文件应重命名/移动自<name>.txtto /foo/<name>.bar.txt(而不是/foo/<name>.txt.bar)。

UPDATE: I manage to find a solution:

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

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

But I wonder if there is a shorter/better solution.

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

采纳答案by glenn Hymanman

In cases like this, a whileloop would be more readable:

在这种情况下,while循环会更具可读性:

find . -name "*.txt" | while IFS= read -r pathname; do
    base=$(basename "$pathname"); name=${base%.*}; ext=${base##*.}
    mv "$pathname" "foo/${name}.bar.${ext}"
done

Note that you may find files with the same name in different subdirectories. Are you OK with duplicates being over-written by mv?

请注意,您可能会在不同的子目录中找到具有相同名称的文件。你同意重复被覆盖mv吗?

回答by fairidox

The following command constructs the move command with xargs, replaces the second occurrence of '.' with '.bar.', then executes the commands with bash, working on mac OSX.

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

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

回答by Santrix

It is possible to do this in one pass (tested in GNU) avoiding the use of the temporary variable assignments

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

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

回答by Ole Tange

If you have GNU Parallel http://www.gnu.org/software/parallel/installed you can do this:

如果你安装了 GNU Parallel http://www.gnu.org/software/parallel/你可以这样做:

find . -name "*.txt" | parallel 'ext="{/}" ; mv -- {} foo/{/.}.bar.${ext##*.}'

Watch the intro videos for GNU Parallel to learn more: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

观看 GNU Parallel 的介绍视频以了解更多信息:https: //www.youtube.com/playlist?list=PL284C9FF2488BC6D1

回答by Michael Campbell

If you're allowed to use something other than bash/sh, AND this is just for a fancy "mv"... you might try the venerable "rename.pl" script. I use it on Linux and cygwin on windows all the time.

如果你被允许使用 bash/sh 以外的东西,而且这只是一个花哨的“mv”......你可以尝试古老的“rename.pl”脚本。我一直在 Linux 上使用它,在 Windows 上使用 cygwin。

http://people.sc.fsu.edu/~jburkardt/pl_src/rename/rename.html

http://people.sc.fsu.edu/~jburkardt/pl_src/rename/rename.html

rename.pl 's/^(.*?)\.(.*)$/-new_stuff_here./' list_of_files_or_glob

You can also use a "-p" parameter to rename.pl to have it tell you what it WOULD HAVE DONE, without actually doing it.

您还可以使用“-p”参数来 rename.pl 让它告诉您它会做什么,而无需实际执行。

I just tried the following in my c:/bin (cygwin/windows environment). I used the "-p" so it spit out what it would have done. This example just splits the base and extension, and adds a string in between them.

我只是在我的 c:/bin (cygwin/windows 环境)中尝试了以下内容。我使用了“-p”,所以它会吐出它会做的事情。这个例子只是将基本和扩展分开,并在它们之间添加一个字符串。

perl c:/bin/rename.pl -p 's/^(.*?)\.(.*)$/-new_stuff_here./' *.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"

回答by kenorb

the files should be renamed/moved from <name>.txtto /foo/<name>.bar.txt

该文件应该被重命名/从移动<name>.txt/foo/<name>.bar.txt

You can use renameutility, e.g.:

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

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

Hint: The subsitution syntax is similar to sedor vim.

提示:替换语法类似于sedor vim

Then move the files to some target directory by using mv:

然后使用以下命令将文件移动到某个目标目录mv

mkdir /some/path
mv *.bar /some/path

To do rename files into subdirectories based on some part of their name, check for:

要根据名称的某些部分将文件重命名为子目录,请检查:

-p/--mkpath/--make-dirsCreate any non-existent directories in the target path.

-p/ --mkpath/--make-dirs创建目标路径中的任何不存在的目录。



Testing:

测试:

$ 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'

回答by Mike Graf

Adding on that the wikipedia articleis surprisingly informative

补充一点,维基百科文章的信息量惊人

for example:

例如:

Shell trick

Another way to achieve a similar effect is to use a shell as the launched command, and deal with the complexity in that shell, for example:

壳把戏

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

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

回答by Vasiliy

Inspired by an answer by @justaname above, this command which incorporates Perl one-liner will do it:

受到上面@justaname 的回答的启发,这个包含 Perl one-liner 的命令可以做到:

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

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