重命名多个文件,通过“_”拆分文件名并保留第一个和最后一个字段
rename multiple files splitting filenames by '_' and retaining first and last fields
假设我有以下文件:
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)/_/' *.txt
但我真正需要做的是实际拆分文件名,所以我想到了awk
,但我不太熟练......这是我目前所拥有的(我知道在某些时候我应该指定 FS="_"
并以某种方式获取第一个和最后一个字段...
find . -name "*.txt" | awk -v mvcmd='mv "%s" "%s"\n' '{old=[=13=]; <<split by _ here somehow and retain first and last fields>>; printf mvcmd,old,[=13=]}'
有什么帮助吗?我没有首选的方法,但是用这个来学习会很好awk
。谢谢!
您的 rename
尝试接近;你只需要确保最后一组是贪婪的。
rename 's/^([^_]*).*_([^_]*[.]txt)$/_/' *_*_*.txt
我在最后一个左括号前添加了一个_
(这是关键修复),在末尾添加了一个$
锚点,并且还扩展了通配符,这样你就不会处理任何不包含至少两个下划线的文件。
Awk 中的等价物可能类似于
find . -name "*_*_*.txt" |
awk -F _ '{ system("mv " [=11=] " " "_" $(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}
做同样的事情,但从字符串的末尾开始修剪。
这个答案适用于您的示例,但@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
使用您展示的示例,请尝试遵循纯 bash 代码(具有 BASH 的强大使用参数扩展能力)。这将捕获名称中带有 name/format .txt 的所有文件。然后它不会选择像这样的文件:a_b.txt
它只会根据要求选择名称中有超过 1 个下划线的文件。
for file in *_*_*.txt
do
firstPart="${file%%_*}"
secondPart="${file##*_}"
newName="${firstPart}_${secondPart}"
mv -- "$file" "$newName"
done
一个不同的 rename
正则表达式
rename 's/(\S_)[a-z_]*(\S\.txt)//'
使用与 sed
相同的正则表达式或在循环中使用 awk
。
for a in a_*; do
name=$(echo $a | awk -F_ '{print , $NF}'); #Or
#name=$(echo $a | sed -E 's/(\S_)[a-z_]*(\S\.txt)//g');
mv "$a" "$name";
done
这是给定示例的替代正则表达式:
$ 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)
假设我有以下文件:
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)/_/' *.txt
但我真正需要做的是实际拆分文件名,所以我想到了awk
,但我不太熟练......这是我目前所拥有的(我知道在某些时候我应该指定 FS="_"
并以某种方式获取第一个和最后一个字段...
find . -name "*.txt" | awk -v mvcmd='mv "%s" "%s"\n' '{old=[=13=]; <<split by _ here somehow and retain first and last fields>>; printf mvcmd,old,[=13=]}'
有什么帮助吗?我没有首选的方法,但是用这个来学习会很好awk
。谢谢!
您的 rename
尝试接近;你只需要确保最后一组是贪婪的。
rename 's/^([^_]*).*_([^_]*[.]txt)$/_/' *_*_*.txt
我在最后一个左括号前添加了一个_
(这是关键修复),在末尾添加了一个$
锚点,并且还扩展了通配符,这样你就不会处理任何不包含至少两个下划线的文件。
Awk 中的等价物可能类似于
find . -name "*_*_*.txt" |
awk -F _ '{ system("mv " [=11=] " " "_" $(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}
做同样的事情,但从字符串的末尾开始修剪。
这个答案适用于您的示例,但@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
使用您展示的示例,请尝试遵循纯 bash 代码(具有 BASH 的强大使用参数扩展能力)。这将捕获名称中带有 name/format .txt 的所有文件。然后它不会选择像这样的文件:a_b.txt
它只会根据要求选择名称中有超过 1 个下划线的文件。
for file in *_*_*.txt
do
firstPart="${file%%_*}"
secondPart="${file##*_}"
newName="${firstPart}_${secondPart}"
mv -- "$file" "$newName"
done
一个不同的 rename
正则表达式
rename 's/(\S_)[a-z_]*(\S\.txt)//'
使用与 sed
相同的正则表达式或在循环中使用 awk
。
for a in a_*; do
name=$(echo $a | awk -F_ '{print , $NF}'); #Or
#name=$(echo $a | sed -E 's/(\S_)[a-z_]*(\S\.txt)//g');
mv "$a" "$name";
done
这是给定示例的替代正则表达式:
$ 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)