重命名多个文件以“_”分割文件名并保留第一个和最后一个字段

Posted

技术标签:

【中文标题】重命名多个文件以“_”分割文件名并保留第一个和最后一个字段【英文标题】:rename multiple files splitting filenames by '_' and retaining first and last fields 【发布时间】:2021-10-29 03:18:03 【问题描述】:

假设我有以下文件:

a_b.txt               a_b_c.txt             a_b_c_d_e.txt         a_b_c_d_e_f_g_h_i.txt

我想以这样一种方式重命名它们,将它们的文件名拆分为_,并保留第一个和最后一个字段,所以我最终得到:

a_b.txt               a_c.txt             a_e.txt         a_i.txt

以为这很容易,但我有点卡住了......

我尝试使用以下正则表达式 rename

rename 's/^([^_]*).*([^_]*[.]txt)/$1_$2/' *.txt

但我真正需要做的是实际拆分文件名,所以我想到了awk,但我对它不是很精通......这是我到目前为止所拥有的(我知道一些我应该指定FS="_" 并以某种方式获取第一个和最后一个字段...

find . -name "*.txt" | awk -v mvcmd='mv "%s" "%s"\n' 'old=$0; <<split by _ here somehow and retain first and last fields>>; printf mvcmd,old,$0'

有什么帮助吗?我没有首选的方法,但是用它来学习awk 会很好。谢谢!

【问题讨论】:

regex 正在 rename 命令中使用(在 OP 的努力以及答案之一中),恕我直言,没有理由删除 regex 标签,所以现在重新添加它。如果您要删除它,请在 cmets 中注明原因,谢谢。 【参考方案1】:

您的rename 尝试已接近;你只需要确保最后一组是贪婪的。

rename 's/^([^_]*).*_([^_]*[.]txt)$/$1_$2/' *_*_*.txt

我在最后一个左括号之前添加了一个_(这是关键修复),并在末尾添加了一个$ 锚,并且还扩展了通配符,这样您就不会处理任何不至少包含两个下划线。

Awk 中的等价物可能看起来像

find . -name "*_*_*.txt" |
awk -F _ ' system("mv " $0 " " $1 "_" $(NF)) '

由于system 调用,这有点脆弱;如果您的文件名可能包含空格或其他 shell 元字符,您可能需要重新考虑您的方法。您可以添加引号以部分解决该问题,但如果文件名包含文字引号,则该命令将失败。你也可以解决这个问题,但是这对我来说有点太复杂了。

这是一种不那么脆弱的方法,它可以处理完全任意的文件名,即使是其中包含换行符的文件名:

find . -name "*_*_*.txt" -exec sh -c 'for f; do
    mv "$f" "$f%%_*_$f##*_"
  done' _  +

find 将在每个文件名之前提供一个前导路径,因此我们在这里不需要mv --(永远不会有一个以破折号开头的文件名)。

parameter expansion$f##pattern 产生变量f 的值,pattern 上的最长可用匹配从一开始就被修剪掉; $f%%pattern 做同样的事情,但从字符串的末尾修剪。

【讨论】:

【参考方案2】:

使用您展示的示例,请尝试遵循纯 bash 代码(具有强大的 BASH 参数扩展能力)。这将捕获名称中包含名称/格式 .txt 的所有文件。然后它不会选择像:a_b.txt 这样的文件,它只会根据要求选择名称中包含超过 1 个下划线的文件。

for file in *_*_*.txt
do
   firstPart="$file%%_*"
   secondPart="$file##*_"
   newName="$firstPart_$secondPart"
   mv -- "$file"  "$newName"
done

【讨论】:

子进程是不必要的;只需使用case $file in *_*_*.txt) 或更改通配符,这样您就可以确定只有至少包含两个下划线的文件才会匹配。 @tripleee,所以你的意思是awk 命令在其中我正在检查名称中- 的数量? 是的,没错。如果您改用for file in *_*_*.txt,则可以将函数体简化为单行(尽管保留临时变量可能会稍微提高可读性)。 @tripleee,谢谢,我现在已经编辑了代码。我将变量保存在代码中,以便任何人都可以轻松理解(通过变量名本身)。【参考方案3】:

此答案适用于您的示例,但@tripleee 的“查找”方法更可靠。

for f in a_*.txt; do mv "$f" "$f%%_*_$f##*_"; done

详情:https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html/https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html

【讨论】:

【参考方案4】:

这是给定示例的替代正则表达式:

$ rename -n 's/_.*_/_/' *.txt
rename(a_b_c_d_e_f_g_h_i.txt, a_i.txt)
rename(a_b_c_d_e.txt, a_e.txt)
rename(a_b_c.txt, a_c.txt)

【讨论】:

【参考方案5】:

不同的rename 正则表达式

rename 's/(\S_)[a-z_]*(\S\.txt)/$1$2/'

使用与sed 相同的正则表达式或在循环中使用awk

for a in a_*; do 
    name=$(echo $a | awk -F_ 'print $1, $NF'); #Or
    #name=$(echo $a | sed -E 's/(\S_)[a-z_]*(\S\.txt)/\1\2/g');  
    mv "$a" "$name"; 
done

【讨论】:

以上是关于重命名多个文件以“_”分割文件名并保留第一个和最后一个字段的主要内容,如果未能解决你的问题,请参考以下文章

使用 PowerShell 重命名 zip 以匹配其内容

如何使用shell复制和重命名多个文件

js 如何批量重命名文件

重命名不带括号的多个文件/删除括号窗口

急!用bat制作批处理文件,复制一个文件到多个以日期命名文件夹下并以“文件夹名+文件本名”的形式重命名

创建多个war文件并用Maven重命名它们