根据正则表达式批量重命名音乐专辑文件夹的 Shell 一行代码 - “zsh:‘+’后缺少标识符”

根据正则表达式批量重命名音乐专辑文件夹的 Shell 一行代码 - “zsh:‘+’后缺少标识符”

我的音乐库包括以以下模式命名的文件夹 :

METAGENRE - Subgenre(s) - ARTIST - Album (year)

我想在文件夹名称中为我拥有其整个唱片目录的一些乐队/艺术家切换年份和专辑“标签”,因此它按发行时间顺序而不是专辑名称排序。与此同时,我还可以删除年份标签周围的括号,即:

METAGENRE - Subgenre(s) - ARTIST - year Album

例如 :METAL - Black, Français - PESTE NOIRE - Split (2018)

应该变成:METAL - Black, Français - PESTE NOIRE - 2018 Split

我对 shell 只有很少的经验。在此处和其他地方寻求帮助,我可以想出这个但不完美的单行(“echo”用于在重命名之前进行测试并避免潜在的地狱,为了可读性,我还在这里重新转录了几行):

% for f in *PESTE\ NOIRE* ;
do echo
     mv "$f"
     "$(echo $f | sed `s/- ([A-Za-z]+) \(([0-9]{4})\)$/- \2 \1/p`)"
     ;
done

在终端中运行此命令时,我得到的只是: zsh: missing identifier after `+'

我究竟做错了什么 ?

编辑:(使用 ' 而不是 ` 引号)

% for f in *PESTE\ NOIRE* ; do echo mv "$f" "$(echo $f | sed 's/- ([A-Za-z]+) \(([0-9]{4})\)$/- \2 \1/')" ; done

有效,但现在我收到另一个错误:

sed: 1: "s/- ([A-Za-z]+) \(([0-9 ...": \2 not defined in the RE

因此 :

% for f in *PESTE\ NOIRE* ; do echo mv "$f" "$(echo $f | sed 's/- \([A-Za-z]+\) \\\(\([0-9]{4}\)\\\)$/- \2 \1/')" ; done

不返回错误,但文件夹保持未重命名状态。

答案1

在 中zsh,您可以用来zmv重命名文件:

autoload -Uz zmv # best in ~/.zshrc
zmv -n '(*PESTE NOIRE*-)(*) \((<1900-2100>)\)' '$1 $3$2'

删除-n(试运行)以实际执行此操作。

您的代码中的一些问题:

  • `...`是 sh/csh 中已弃用的命令替换形式,并且与现代$(...)形式(来自ksh)一样,也需要引用。但不应该使用它,因为它本身存在许多问题。无论如何,它不用于引用文本,仅用于扩展某些命令的输出。对于sed往往包含诸如\或之类字符的代码,最好的引号$是单引号 ( '...'),在类似于 Bourne 的 shell(包括 zsh)中,shell 不会对其中的任何字符进行特殊处理。
  • 要逐字打印一些文本,请使用print -r - $f(ksh 样式,尽管您需要 ksh 中的引号) 或printf '%s\n' $f(POSIX 样式,也需要 sh 中的引号),或echo -E - $f( zsh-特定) 不echo $f破坏$f-或包含开头的值反斜杠。另请参阅<<< $f此处字符串语法。
  • +,{4}(...)是扩展(或perl)正则表达式运算符,因此您需要-E或, 在支持的情况下-P的选项sed。基本的正则表达式等效项是\{1,\}, \{4\}and \(...\)(这解释了为什么 your\2不匹配)。一些sed实现还支持\+.
  • [A-Za-z]A匹配在andZaand之间排序的字符z,具体取决于 sed 实现,它可能会或可能不会匹配çor œ,它不与 zsh 自己的 glob 匹配。ź可能会排序,z因此被排除在外。其他文字中的字母也是如此。用于[[:alpha:]]匹配字母(或更普遍地用于人类语言单词中的字符)。许多专辑可能有空格或数字或.'...用于[^-]除此之外的任何字符-可能是更好的方法。或者像zmv上面一样,依赖于前面的内容*是贪婪的这一事实。
  • 请记住,这sed是基于行和基于文本的,因此不会将文件名作为一个整体进行处理,而是单独处理文件名的每一行(仅对于包含换行符或非字符(例如编码中的字符)的文件名来说是一个问题区域设置以外的字符集)。
  • 在命令参数中使用变量数据时,需要--标记选项的结尾。如果它以 开头,mv $f ...则将视为一个选项。$f-

因此,如果您必须使用sed(但您肯定不会在 zsh 中使用),那么解决这些问题将如下所示:

for old in *'PESTE NOIRE'*; do
  new=$(print -r - $f | sed  '
    :1
    $!{
      N;b1
    }
    s/\(.*-\)\(.*\) ([[:digit:]]\{4\})$/\1 \3\2/'
  ) && [[ $old != $new ]] &&
    mv -i -- $old $new
done

答案2

使用单引号 (') 而不是反引号 (`) 来引用 sed 表达式

相关内容