我需要 shell 脚本,它将一些字符串添加到目录和子目录中的文件名(.c 文件)中。
例如: 如果lokesh
是父目录且位于其内部lokesh1
,则lokesh2
是子目录且位于lokesh1
( 1.c
, 2.c
...) 和内部lokesh2
( a.c
, b.c
...)。因此,运行脚本后我想将其更改为(x_1.c
,x_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
(或者不像-execdir
OpenBSD 那样):
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
无论您使用的实现如何以及是否使用-exec
or -execdir
,它都有效,并且也适用于递归通配符:
rename -n 's|[^/]+$|x_$&|'
这会匹配至少 1 个不在路径/
末尾 ( ) 的字符,然后在添加的 后重现整个匹配 ( ) 。这个命令$
$&
x_
应该可以在任何地方工作find
并且可以使用 Perl 重命名:
find . -type f -name "*.c" -exec rename -n 's|[^/]+$|x_$&|' {} +