用sed替换数字之间的字符

时间:2017-06-29 21:39:36

标签: macos sed rename

给定一个.txt文件,其中包含名为的文件列表:

2015_273_2_1_1__Your_Favorite_Home_Movie_1.mov
2015_273_2_1_1__Your_Favorite_Home_Movie_2.mov
2015_273_2_1_1__Your_Favorite_Home_Movie_3.mov

如何将数字之间的所有 _ 重命名为 . 而不更改字母之间的数字?

我希望输出为:

2015.0273.2.1.1__Your_Favorite_Home_Movie_1.mov
2015.0273.2.1.1__Your_Favorite_Home_Movie_2.mov
2015.0273.2.1.1__Your_Favorite_Home_Movie_3.mov

sed -E '/s/{0..9}_{0..9}/{0..9}.{0..9}/' /Users/medialab/Desktop/ls.txt 

给我一​​个错误invalid command code .

在我问的时候,如果目标是目录中的文件而不是rename文件中的文件列表,那么这对mv.txt有什么用处?

3 个答案:

答案 0 :(得分:2)

TL; DR:以下正则表达式执行替换:

sed -E 's/([0-9]{4})_([0-9]{3})_([0-9])_([0-9])_([0-9])/\1.0\2.\3.\4.\5/' ls.txt
  • 使用正则表达式捕获范围之间的字符是[0-9]而不是{0..9}
  • 使用正则表达式进行字符串替换时,可以使用括号定义的匹配组。您可以使用\n来引用这些数字,其中 n 是匹配组的编号(整个匹配为0)。

    s/(foo)bar/\1-\0/ => foo-foobar
    
  • 此外,sed命令不以/开头。

  • 最后,当你想要正则表达式中有一定数量的字符时,你应该使用*(O到N),+(1到N)或字符数你想要{MIN,MAX}

注意: -E选项用于扩展正则表达式,这是一种更常见的正则表达式。您还可以在Linux上使用等效的-r选项。

答案 1 :(得分:0)

OSX没有像Linux这样的内置rename命令。如果您从外部源(如自制软件或macports或fink等)安装一个,那么确定您正在使用哪一个以及它的选项是有帮助的。我会在不参考rename的情况下回答,因为它不会对使用公共广告的普通macOS有用。 (说实话,Linux也没有一个rename - 我知道在不同的Linux发行版中有三个不同的rename命令,所有这些命令都有不同的选项和用法。)

我可能会使用awk而不是sed:

$ awk 'BEGIN{FS=OFS="__"} {f=$0; gsub(/_/,".",$1); printf "mv %s %s\n", f, $1 $2}' list.txt
mv 2015_273_2_1_1__Your_Favorite_Home_Movie_1.mov 2015.273.2.1.1Your_Favorite_Home_Movie_1.mov
mv 2015_273_2_1_1__Your_Favorite_Home_Movie_2.mov 2015.273.2.1.1Your_Favorite_Home_Movie_2.mov
mv 2015_273_2_1_1__Your_Favorite_Home_Movie_3.mov 2015.273.2.1.1Your_Favorite_Home_Movie_3.mov

为便于阅读而分手,即:

  • BEGIN{FS=OFS="__"} - 将字段分隔符设置为双下划线
  • {f=$0; - 存储原始文件名
  • gsub(/_/,".",$1); - 用__
  • 左侧的点替换下划线
  • printf "mv %s %s\n", f, $1 $2}' - 打印我们的命令。如果看起来正确,请通过/bin/sh进行管道。

请注意,如果您想要使用标点符号和特殊字符,那么这就不是一种方法。以下内容取决于由${var/re/text}构造引起的bash,它不是POSIX的一部分。

while read file; do
  [ -f "$file" ] || continue     # skip files that don't exist
  lhs="${file%__*}"
  lhs="${lhs//_/.}"
  echo mv -v "$file" "${lhs}__${file#*__}"
done < list.txt

虽然时间较长,但也更容易阅读和维护。你可以包含一些关于跳过不存在的文件的逻辑,这很好。

答案 2 :(得分:0)

GNU sed

$ sed -r ':loop;s/([0-9])_([0-9])/\1.\2/;tloop' input.txt
2015.273.2.1.1__Your_Favorite_Home_Movie_1.mov
2015.273.2.1.1__Your_Favorite_Home_Movie_2.mov
2015.273.2.1.1__Your_Favorite_Home_Movie_3.mov
# add a 0 before the month.
$ sed -r ':loop;s/([0-9])_([0-9])/\1.\2/;tloop;s/[0-9]+/0&/2' input.txt
2015.0273.2.1.1__Your_Favorite_Home_Movie_1.mov
2015.0273.2.1.1__Your_Favorite_Home_Movie_2.mov
2015.0273.2.1.1__Your_Favorite_Home_Movie_3.mov

修改

如果目标是目录中的文件,只需执行以下操作:

# origion files.
$ find . -name '*.mov'
./2015_273_2_1_1__Your_Favorite_Home_Movie_1.mov
./2015_273_2_1_1__Your_Favorite_Home_Movie_2.mov
./2015_273_2_1_1__Your_Favorite_Home_Movie_3.mov
# if don NOT use the e modifier of s command, we can get the final command.
$ find . -name '*.mov' |sed -r 'h;:loop;s/([0-9])_([0-9])/\1.\2/;tloop;s/[0-9]+/0&/2;G;s/([^\n]+)\n(.+)/mv \2 \1/'
mv ./2015_273_2_1_1__Your_Favorite_Home_Movie_1.mov ./2015.0273.2.1.1__Your_Favorite_Home_Movie_1.mov
mv ./2015_273_2_1_1__Your_Favorite_Home_Movie_2.mov ./2015.0273.2.1.1__Your_Favorite_Home_Movie_2.mov
mv ./2015_273_2_1_1__Your_Favorite_Home_Movie_3.mov ./2015.0273.2.1.1__Your_Favorite_Home_Movie_3.mov
# use e modifier of s command to execute the substituted command.
$ find . -name '*.mov' |sed -r 'h;:loop;s/([0-9])_([0-9])/\1.\2/;tloop;s/[0-9]+/0&/2;G;s/([^\n]+)\n(.+)/mv \2 \1/e'
# changed files.
$ find . -name '*.mov'
./2015.0273.2.1.1__Your_Favorite_Home_Movie_1.mov
./2015.0273.2.1.1__Your_Favorite_Home_Movie_2.mov
./2015.0273.2.1.1__Your_Favorite_Home_Movie_3.mov

说明:

  1. 使用h命令将原始文件名保存在hold space
  2. 使用:loop;s/([0-9])_([0-9])/\1.\2/;tloop;s/[0-9]+/0&/2重命名_numerals之间的所有.,并在月之前添加0
  3. 使用G命令将hold space附加到pattern space,然后pattern space中的内容为CHANGED_FILENAME\nORIGIN_FILENAME
  4. 使用s/([^\n]+)\n(.+)/mv \2 \1/将命令与CHANGED_FILENAMEORIGIN_FILENAME合并;
  5. 最后使用e命令的s修饰符执行上面汇编的命令,这将完成实际工作。
  6. P.S:

    1. sed -r 's/([0-9])_([0-9])/\1.\2/g'无法处理连续匹配结果,例如:3_2_12_1未替换)。
    2. -E用于BSD sed