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
Modifying replace string in xargs
提问by betabandido
When I am using xargs
sometimes 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 /foo
and it will append the extension bar
to 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>.txt
to /foo/<name>.bar.txt
(instead of /foo/<name>.txt.bar
).
如果我不想在替换字符串中附加一些文本,而是想修改该字符串,以便可以在文件的名称和扩展名之间插入一些文本,我该怎么做?例如,假设我想做与上一个示例相同的操作,但文件应重命名/移动自<name>.txt
to /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 while
loop 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>.txt
to/foo/<name>.bar.txt
该文件应该被重命名/从移动
<name>.txt
到/foo/<name>.bar.txt
You can use rename
utility, e.g.:
您可以使用rename
实用程序,例如:
rename s/\.txt$/\.txt\.bar/g *.txt
Hint: The subsitution syntax is similar to sed
or vim
.
提示:替换语法类似于sed
or 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-dirs
Create 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
补充一点,维基百科文章的信息量惊人
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