我有一个 10K mp4 文件的列表,其中一些包含 ?标记。我需要将它们替换为 _(下划线)。
我可以使用 find 命令来查找这些文件,但不确定是否可以重命名它们。
find . -name "*\?*.mp4" -exec mv {} XXXXXXX \;
看起来我必须迭代 find 返回的列表,并且对于每个我需要替换的列表?与 _ 但我不知道该怎么做。
答案1
不要用于find
此 - 无论您采用哪种方式,您都需要类似于每个文件find
一个的东西。mv
这是很多过程,而且没有任何好处。更不用说这样做更困难。无论如何,这就是我的意见。
我会使用流或批处理工具,并且倾向于更喜欢pax
:
cd -P . && mkdir ../newmp4 &&
pax -rwls'|?|_|g' . "${PWD%/*}/newmp4"
如果您可以创建该目录../newmp4
,该小脚本将镜像植根于刚刚使用硬链接创建的目录的树,但同时.
会用下划线替换任何文件名中出现的每个内容。?
这样做的一个好处是两个都此后树继续存在 - 这些文件的当前目录中的根目录都不会受到影响 - 它们仅在树的镜像版本中更改。因此,您可以在决定删除哪个版本的树之前检查结果。如果手术过程中出现任何问题,也不会造成任何伤害。
然而,这确实需要一个支持硬链接的文件系统,并且其剩余空闲 inode 的数量至少与 +2 的子目录一样多.
,并且假设..
.
和 的所有子目录.
共享相同的文件系统挂载。
我认为,实际上可能会得到相同的效果rsync
,尽管我不认为它是那么简单。如果你没有pax
(即使你真的 应该),你就可以得到它。米拉比洛斯维持(据我所知)最多常用版本在Linux系统上。
答案2
就我个人而言,我认为这只是对 perl 的要求,特殊字符安全和重命名是系统调用而不是 fork exec。
find . -name "*\?*.mp4" -print0|perl -n0e 'next if /\?.*\//;$o=$_;s!\?!_!g;next if -e;rename $o, $_'
忽略带有 ? 的目录中的文件以防止未定义的行为并检查重命名冲突。请参阅下面的详细说明。
find . \ # find files below the current directory
-name "*\?*.mp4" \ # having a ? in the name and ending in .mp4
-print0\ # print the file names separated by nulls
|perl \ # direct the output of find to the input of perl
-n \ # loop over standard input while assigning it to $_
-0 \ # lines from standard input are separated by null
-e \ # evaluate the next argument as perl commands
'next if /\?.*\//; # skip this file if the directory name includes ?
$o=$_; # make a copy of the file name of preserve the name
s!\?!_!g; # change the ?s in the filename to _s
next if -e; # skip file if there is a file by that name
rename $o, $_' # do the rename
答案3
我发现最简单的方法是通过管道输入其他内容来生成命令,例如sed
,然后执行 中的命令sh
。
find . -name '*\?*.mp4' -print | sed 's/.*/"&";h;y/?/_/;x;G;s/\n/ /;s/^/mv /' | sh -s
是find
非常不言自明的,就像sh
.该sed
命令可能需要一些解释:
s/.*/"&"/
用双引号将输入括起来以处理任何空格或特殊字符;h
将缓冲区复制到“hold”缓冲区,保存副本;y/?/_/
tr \? _
与命令行上的相同;x
将“保持”缓冲区的内容与模式缓冲区交换;G
使用“\n”分隔符将“hold”缓冲区附加到模式缓冲区的末尾;我们现在有“stringwith?\nstringwith_”;s/\n/ /
替换换行符;s/^/mv /
前置mv
命令。
这将为使用旧名称和新名称mv
找到的每个文件生成命令。find
答案4
和zsh
:
autoload zmv # best in ~/.zshrc
zmv '(**/)(*\?*.mp4)' '$1${2//\?/_}'
这排除了隐藏的目录,并且不会查看隐藏目录的内部。它将重命名这些文件,无论其类型如何(常规、目录、符号链接...)。
如果您还想考虑隐藏文件/目录,则仅重命名 mp4 文件(如果它们是常规文件)并且不区分大小写,那就是:
zmv '(**/)(*\?*.(#i)mp4)(#q.D)' '$1${2//\?/_}'
GNU 近似(不会像zmv
以前那样检查冲突和覆盖):
find . -iname '*\?*.mp4' -type f -exec bash -c '
for file do
base=${file##*/}
mv -i "$file" "${file%/*}/${base//\?/_}"
done' {} +