【问题标题】:bash removing part of a file namebash 删除文件名的一部分
【发布时间】:2014-03-08 15:35:14
【问题描述】:

我有以下格式的文件:

$ ls CombinedReports_LLL-*'('*.csv
CombinedReports_LLL-20140211144020(Untitled_1).csv
CombinedReports_LLL-20140211144020(Untitled_11).csv
CombinedReports_LLL-20140211144020(Untitled_110).csv
CombinedReports_LLL-20140211144020(Untitled_111).csv
CombinedReports_LLL-20140211144020(Untitled_12).csv
CombinedReports_LLL-20140211144020(Untitled_13).csv
CombinedReports_LLL-20140211144020(Untitled_14).csv
CombinedReports_LLL-20140211144020(Untitled_15).csv
CombinedReports_LLL-20140211144020(Untitled_16).csv
CombinedReports_LLL-20140211144020(Untitled_17).csv
CombinedReports_LLL-20140211144020(Untitled_18).csv
CombinedReports_LLL-20140211144020(Untitled_19).csv

我希望删除此部分:
20140211144020(这是运行报告的时间戳,因此会有所不同)

最后得到类似的东西:

CombinedReports_LLL-(Untitled_1).csv
CombinedReports_LLL-(Untitled_11).csv
CombinedReports_LLL-(Untitled_110).csv
CombinedReports_LLL-(Untitled_111).csv
CombinedReports_LLL-(Untitled_12).csv
CombinedReports_LLL-(Untitled_13).csv
CombinedReports_LLL-(Untitled_14).csv
CombinedReports_LLL-(Untitled_15).csv
CombinedReports_LLL-(Untitled_16).csv
CombinedReports_LLL-(Untitled_17).csv
CombinedReports_LLL-(Untitled_18).csv
CombinedReports_LLL-(Untitled_19).csv

我只是按照mv 命令的思路思考,可能是这样的:

$ ls CombinedReports_LLL-*'('*.csv

但也许sed 命令或其他命令会更好

【问题讨论】:

标签: bash shell sed rename mv


【解决方案1】:

renameperl 包的一部分。它根据 perl 风格的正则表达式重命名文件。从文件名中删除日期:

rename 's/[0-9]{14}//' CombinedReports_LLL-*.csv

如果rename不可用,可以使用sed+shell

for fname in Combined*.csv ; do mv "$fname" "$(echo "$fname" | sed -r 's/[0-9]{14}//')" ; done

以上循环遍历您的每个文件。对于每个文件,它执行一个mv 命令:mv "$fname" "$(echo "$fname" | sed -r 's/[0-9]{14}//')",在这种情况下,sed 能够使用与上述rename 命令相同的正则表达式。 s/[0-9]{14}// 告诉 sed 连续查找 14 位数字并将其替换为空字符串。

【讨论】:

  • 不需要 sed——现代 shell 可以内置自己的替换。
  • @CharlesDuffy (1) 我通常致力于 Dash 兼容性,不仅因为它比 bash 快得多,而且因为普通 shell 得到更广泛的支持。 (2) 虽然 Bash 可以进行原始替换,但 sed 的正则表达式最终要强大得多。
  • POSIX sh 和 dash 中可用的 PE 足以完成这项特定工作。当然,您不能进行替换,但您可以过滤开头和结尾的字符串并将它们组合起来。至于 sed 的正则表达式更强大的说法——bash 也具有内置的正则表达式支持(请参阅BASH_REMATCH),并且它的 glob 的功能与 BRE 相当(请参阅extglob)。
  • ...不过,最大的区别在于效率;如果您要迭代数百个文件,则分叉子shell、在其中执行 sed、等待()完成该子shell、读取()其输出等的时间损失会很快增加。
  • ...此外,使用命令替换也有副作用——任何时候使用$(foo)IFS 中的尾随字符都会被删除,因此以空格结尾的文件名将包含该空格被剥夺,即使那不是你想要的行为。
【解决方案2】:

不使用 renamesed 等其他工具,并严格遵守 bash

for f in CombinedReports_LLL-*.csv
do
  newName=${f/LLL-*\(/LLL-(}
  mv -i "$f" "$newName"
done

【讨论】:

  • +1。但是为什么不直接把 all 放在 mv 命令中呢? mv -i "$f" "${f/LLL-*\(/LLL-(}"
  • 为什么?当然是为了清楚。深入了解其他程序员的内心和想法,并猜测哪种解决方案最能被理解。我的猜测是,顺便提一下 OP 提出的问题,他会用像 newName 这样的口语变量名更好地理解它。
  • 这是一个标准的正则表达式吗?还是一些特定于 bash 的类型?我找不到任何关于 bash 特定正则表达式语言的文档,但不明白为什么重复 LLL 或第二个左括号。
  • 它不是一个正则表达式。它或多或少是一个全局模式的替代品。查看man bash 并搜索Pattern substitution
【解决方案3】:
for f in CombinedReports_LLL-* ; do
    b=${f:0:20}${f:34:500}
    mv "$f" "$b"
done

你可以在shell上逐行尝试:

f="CombinedReports_LLL-20140211144020(Untitled_11).csv"
b=${f:0:20}${f:34:500}
echo $b

【讨论】:

  • 希望所有数字(时间戳)的宽度相同。尽管如此,任何阅读该代码的人都必须猜测这些常量(02034500)来自哪里以及它们的含义。这当然不是不可能猜到的,但也不是最清楚的表达方式。
【解决方案4】:

您可以为此使用rename 实用程序。它使用类似于 sed 的语法来更改文件名。以下示例(来自 rename 手册页)显示了如何从本地目录中的备份文件列表中删除尾随的“.bak”扩展名:

rename 's/\.bak$//' *.bak

【讨论】:

  • 目前在我的 cygwin 版本中没有rename 命令。还有其他方法吗?
【解决方案5】:

我正在使用顶部响应中给出的建议,并将以下行放入 shell 脚本中:

ls *.nii | xargs rename 's/[f_]{2}//' f_0*.nii

在终端中,此行完美运行,但在我的脚本中它不会执行并将 * 作为文件名的文字部分读取。

【讨论】:

    猜你喜欢
    • 2017-09-16
    • 2022-11-17
    • 2017-04-08
    • 2011-12-03
    • 2012-01-11
    • 2017-01-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多