重命名递归目录中存在的所有以 .c 结尾的文件

重命名递归目录中存在的所有以 .c 结尾的文件

我需要 shell 脚本,它将一些字符串添加到目录和子目录中的文件名(.c 文件)中。

例如: 如果lokesh是父目录且位于其内部lokesh1,则lokesh2是子目录且位于lokesh1( 1.c, 2.c...) 和内部lokesh2( a.c, b.c...)。因此,运行脚本后我想将其更改为(x_1.cx_2.c...)和(x_a.c, x_b.c...)。

答案1

笔记:在编写下面的内容时,我使用的是 OpenBSD,find它将-execdir utility {} ';'替换{}为找到的文件的基本名称。对于 GNU find{}也将是找到的文件的基本名称,但它还会附加前缀./,这使得下面的第一个find命令无用。对于 GNU 用户find(例如大多数使用 Linux 的人),请向下滚动到解决方案的其他变体。


您想要查找目录.c中或目录下的所有文件lokesh,并在其名称前面加上x_.

find lokesh -type f -name '*.c' -execdir echo mv {} x_{} ';'

-type f和表达式-name '*.c'将查找所有相关文件,同时-execdir mv {} x_{} ';'将重命名找到的文件。

-execdir表达式不是标准的,但大多数实现都find支持它。它的不同之处在于-exec,给定的实用程序是以找到的路径名的父目录作为工作目录来执行的。因此,命令行中的{}将会是我们要重命名的文件的基本名称(而不是像 那样的完整路径名-exec)。

运行一次,然后echo当您看到它执行正确的操作时将其删除。这echo将阻止mv实际重命名文件。


在不支持的系统上-execdir(或者不像-execdirOpenBSD 那样):

find lokesh -type f -name '*.c' \
    -exec sh -c 'for name do echo mv "$name" "${name%/*}/x_${name##*/}"; done' sh {} +

或者,更短但效率稍低,

find lokesh -type f -name '*.c' \
    -exec sh -c 'echo mv "$1" "${1%/*}/x_${1##*/}"' sh {} ';'

这两种变体都使用了一个简短的 shell 脚本,最终基本上完成了相同的事情:

mv "$name" "${name%/*}/x_${name##*/}"

这会将给定完整路径名的文件移动到同一目录中$name前缀为 的新名称。x_参数${name%/*}替换相当于$( dirname "$name" )and will 给出路径名的父目录,而${name##*/}相当于$( basename "$name" )which 给出路径名的基本名称(文件名部分)。

答案2

您可以使用 Perl 重命名(如果可用)。通过 Perl 重命名,我的意思是 prename 或 file-rename,而不是 rename.ul,它可能rename指向您的系统,如果您尝试将它与我在下面建议的代码参数一起使用,它肯定会出错...

在我的系统上(Ubuntu 17.10 - 我安装了rename软件包以获取 Perl 重命名):

$ readlink -e $(type -P rename)
/usr/bin/file-rename

这是我测试这个答案的唯一环境!

如果您只有两个目录级别(如示例所示),您可以从lokesh目录运行如下所示:

rename -n 's|/|/x_|' */*.c

始终首先使用该-n标志运行,该标志显示如果您在没有-n.如果您看到正确的结果,请删除-n.

如果您有复杂的目录结构,则可以使用递归通配符(如果可用)。在重击中:

shopt -s globstar

然后尝试rename

rename -n 's|(.*)/|$1/x_|' ./**/*.c

这会捕获最后一个路径元素之前的所有内容(.*)/,并使用反向引用重现它$1

否则(如果您不能使用递归通配符),请使用find

find . -type f -name "*.c" -exec rename -n 's|(.*)/|$1/x_|' {} +

虽然如果文件位于许多目录中,速度会更慢,但您也可以使用它允许更简单的正则表达式(我发现(不到一秒)和(3-4 秒)-execdir之间有明显的速度差异,约 1113个文件在约 175目录):-exec-execdir.c

find . -type f -name "*.c" -execdir rename -n 's|/|/x_|' {} +

结果-n看上去有些令人困惑,-execdir但是如果基本名称看起来正确,那就没问题。

然而,感谢一个库萨拉南达评论,我意识到这取决于find所有-execdir参数都以 开头的GNU ./,而不是不以 开头的 OpenBSD 版本。如果文件名根本没有路径前缀,那么我猜你可以使用这个版本:

find . -type f -name "*.c" -execdir rename -n -- 's|^|x_|' {} +

但我无法测试它,因为我没有 OpenBSD find

然而,埃利亚·卡根非常有帮助想出了这个更好的正则表达式find无论您使用的实现如何以及是否使用-execor -execdir,它都有效,并且也适用于递归通配符:

rename -n 's|[^/]+$|x_$&|'

这会匹配至少 1 个不在路径/末尾 ( ) 的字符,然后在添加的 后重现整个匹配 ( ) 。这个命令$$&x_应该可以在任何地方工作find并且可以使用 Perl 重命名:

find . -type f -name "*.c" -exec rename -n 's|[^/]+$|x_$&|' {} +

相关内容