使用find命令重命名文件

使用find命令重命名文件

我有一个 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' {} +

相关内容