正在尝试重命名递归目录中的某些文件类型



我在一个目录结构中有一堆文件,例如:

Dir
SubDir
File
File
Subdir
SubDir
File
File
File

很抱歉格式混乱,但正如你所看到的,有不同目录级别的文件。所有这些文件名后面都附加了一个由7个数字组成的字符串,例如:1234567_filename.ext。我正在尝试删除文件名开头的数字和下划线。

现在我正在使用bash,并使用这个oneliner使用mv和cut:重命名文件

for i in *; do mv "$i" "$(echo $i | cut -d_ -f2-10)"; done

这是在我CD插入目录时运行的。我很想找到一种递归的方法,这样它只重命名文件,而不是文件夹。我还在shell中使用了foreach循环,在bash之外,用于那些有一堆文件夹的目录,其中有文件,没有其他子目录:

foreach$ set p=`echo $f | cut -d/ -f1`
foreach$ set n=`echo $f | cut -d/ -f2 | cut -d_ -f2-10`
foreach$ mv $f $p/$n
foreach$ end

但只有当文件夹中没有其他子目录时,这才有效。

有没有一个循环或oneliner可以用来重命名目录中的所有文件?我甚至尝试使用find,但不知道如何将cut合并到代码中。

非常感谢您的帮助。

使用Perl的重命名(独立命令):

shopt -s globstar
rename -n 's|/[0-9]{7}_([^/]+$)|/$1|' **/*

如果一切看起来都很好,请删除-n

globstar:如果设置,路径名扩展上下文中使用的模式**将匹配所有文件以及零个或多个目录和子目录。如果该模式后面跟着一个/,只有目录和子目录火柴

bash确实提供了函数,这些函数可以是递归的,但您不需要递归函数来完成此任务。您只需要枚举树中的所有文件。find命令可以做到这一点,但打开bashglobstar选项并使用shell glob更安全:

#!/bin/bash
shopt -s globstar
# enumerate all the files in the tree rooted at the current working directory
for f in **; do
# ignore directories
test -d "$f" && continue
# separate the base file name from the path
name=$(basename "$f")
dir=$(dirname "$f")
# perform the rename, using a pattern substitution on the name part
mv "$f" "${dir}/${name/#???????_/}"
done

请注意,这并不能验证文件名是否与执行重命名之前指定的模式匹配;我相信你的话,他们确实这么做了。如果需要这样的支票,那么肯定可以添加。

对您现有的进行这个小调整怎么样

for i in `find . -type f`; do mv "$i" "$(echo $i | cut -d_ -f2-10)"; done

基本上只是用find替换*-类型f`

应该可以使用find。。。

find -E . -type f 
-regex '.*/[0-9]{7}_.*.txt' 
-exec sh -c 'f="${0#*/}"; mv -v "$0" "${0%/*}/${f#*_}"' {} ;

您的find选项可能有所不同——我在FreeBSD中这样做。这里的想法是:

  • -E指示find使用扩展正则表达式
  • -type f导致只能找到正常的文件(而不是目录或符号链接)
  • -regex ...与您要查找的文件相匹配。如果需要的话,你可以更具体一些
  • exec ... ;运行一个命令,使用{}(我们找到的文件)作为参数

我们正在运行的命令首先使用参数扩展来获取目标目录,然后删除文件名。请注意临时变量$f,它用于解决额外下划线成为文件名一部分的可能性。

请注意,这不是一个bash命令,尽管您当然可以从bash shell运行它。如果您想要一个不需要使用像find这样的外部工具的bash解决方案,您可以执行以下操作:

$ shopt -s extglob   # use extended glob format
$ shopt -s globstar  # recurse using "**"
$ for f in **/+([0-9])_*.txt; do f="./$f"; echo mv "$f" "${f%/*}/${f##*_}"; done

这使用与find解决方案相同的逻辑,但使用bash v4extglob提供更好的文件名匹配,并使用globstar在子目录中递归。

希望这些帮助。

最新更新