Bash正则表达式重命名文件

3

我有一堆文件,它们只是Unix时间戳。问题在于它们的文件名是以毫秒为单位的,而扫描目录的程序希望它们以秒为单位。因此,我需要重命名所有文件,删除.txt扩展名前面的最后3个数字。例如:

1461758015598.txt --> 1461758015.txt

我对正则表达式和Bash不熟悉,所以不知道该如何操作。


一种递归工作的Python备用方案:https://dev59.com/F2gv5IYBdhLWcg3wW_h_#39698169 - ccpizza
2个回答

3
这里有一种方法:
for f in *.txt; do   
  mv "$f" "${f/[0-9][0-9][0-9].txt/.txt}"
done

dodone 之间,它一次只查看一个文件,并且当前文件的名称在参数 f 中。 "$f" 是该参数的值,未更改。 "${f/[0-9][0-9][0-9].txt/.txt}" 是应用替换后的参数值:将“三个数字后跟.txt”替换为仅.txt

正如 @anubhava 指出的那样,如果您运行超过一次,它将继续从文件名中删除数字,因此不要这样做。您可以通过收紧全局模式来使其幂等。这有点笨重:

for f in [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].txt; do   
  mv "$f" "${f/[0-9][0-9][0-9].txt/.txt}"
done

所以您可能需要使用外部工具来使其更紧凑(这假定目录中没有任何文件名中包含空格或换行符):

for f in $(ls -1 | egrep '^[0-9]{13}\.txt$'); do 
  mv "$f" "${f/[0-9][0-9][0-9].txt/.txt}"
done

您可以使用while read循环将限制减少到仅限于“无换行符”:

ls -1 | egrep '^[0-9]{13}\.txt$' | while read f; do
  mv "$f" "${f/[0-9][0-9][0-9].txt/.txt}"
done

4
OP 只需要运行它一次,否则它还会将 "1461758015.txt" 重命名为 "1461758.txt",然后将 "1461758.txt" 重命名为 "1461.txt"。 - anubhava
如果OP已经在使用bash,那么使用ls +([0-9]).txt | egrep ...的extglob可能更安全,并且在非常大的目录中性能更高。 - ghoti

0

Mark的答案涵盖了许多选项。

但是,如果您想要递归的东西,我会选择像这样基于find的解决方案:

find -E . -regex '.*/[0-9]{13}\.txt' -exec \
  sh -c 'set -; mv -v "$0" "${0%???.txt}.txt"' "{}" \;

注意:

  • 我正在使用FreeBSD,它有一个-E选项,告诉-regex使用ERE。如果您使用的是旧操作系统,则可能需要将ERE转换为BRE。
  • ERE坚持13位数字。这可以避免anubhava担心如果您运行转换超过一次会导致递减增加。
  • 对于参数扩展,我使用${..%..}而不是${../../},使其符合POSIX标准而不是依赖于bash。(当然,在bash中也可以工作,但它不需要。)
  • 您可能想知道为什么匹配只是剥离单个通配符字符而不是数字。这很好-对我来说更容易输入,并且我们已经将文件名约束设置为find选项的一部分。

如果您希望避免使用find但仍希望在bash中进行递归处理,则可以使用globstar选项:

shopt -s globstar extglob
for f in **/+([0-9]).txt; do
  [[ $f =~ ^[0-9]{13}\.txt$ ]] && mv -v "$f" "${f%...\.txt}.txt"
done

请注意,globstar 依赖于 Bash 版本 4。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接