0

我正在尝试编写一个简单的 bash 脚本,以允许我重命名一些格式错误的电视节目文件名(并且未被 xbmc 拾取)。我能够让 awk 命令打印出我想要的输出,但是一旦我把它放在反引号(或重音字符)中以便我可以合并 mv 命令,它就会出错。

我正在使用的 awk 命令如下(上面有一个简单的 for 循环来遍历目录中的所有文件)。

echo $i | awk -F" " '{print $1 "\\ -\\ S0" $3 "E" $5 ".avi"}'

但是,当我向其中添加 mv 命令时,

mv $i `echo $i | awk -F" " '{print $1 "\\ -\\ S0" $3 "E" $5 ".avi"}'`

它出现以下错误。

awk: warning: escape sequence `\ ' treated as plain ` '

据我所知,我需要其中的反斜杠才能使 mv 命令正常工作。

文件名的输入格式是

Showname Season 1 Episode 01 - Episode Title.avi
Show Name Season 1 Episode 01 - Episode Title.avi

我希望它是

Showname - S01E01 - Episode Title.avi
Show Name - S01E01 - Episode Title.avi

我不太担心包含剧集标题(我找不到任何简单的方法来包含所有额外的字段,而且我有另一个程序用来移动和命名我的所有文件,无论如何都会把标题放在那里) . 有些节目的名字中有两个单词(所以它可能是节目名称),所以我的计划是手动调整脚本以使用各个字段。

有没有关于如何让 awk 与文件名正常工作以防止在我使用“反引号”时出现错误的建议?

4

3 回答 3

4

请注意,由于您的文件名包含空格(或者至少是有趣的),您应该"$i"mv命令中使用双引号,并在echo. 如果文件名中只有单个空格并且没有制表符或换行符,则您在回显中“摆脱它”,但是如果您不将文件名括在双引号中并且它确实包含两个相邻的空格,则会破坏文件名,或前导空格,或尾随空格,或......通常,$(...)也使用而不是反引号。

通过提供几个(一或两个)示例输入文件名和预期的相应输出文件名,您的问题将得到改善。我们所能知道的是,当您使用空格进行拆分时(-F" "似乎几乎没有必要),文件名至少有 5 个部分,其中只保留第一个、第三个和第五个部分。您可能在输出中生成反斜杠以保留名称中的空格,而不是因为您认为文件名中的反斜杠是个好主意。(也就是说,反斜杠不会出现在磁盘上的名称中。)

既然你有一个循环,我这样做的方式awk是:

for file in *
do
    name=$(echo "$file" | awk '{print $1 " - S0" $3 "E" $5 ".avi"}')
    mv "$file" "$name"
done

这样做有充分的理由。分配保留了输出中的空格等awk(砍掉尾随的换行符,但仅此而已)。然后mv命令行很简单,使用双引号来防止出现问题。

试:

for file in *
do
    mv "$file" $(echo "$file" | awk '{print $1 " - S0" $3 "E" $5 ".avi"}')
done

让您陷入麻烦,因为管道的输出被 shell 重新扫描并拆分,您必须防止拆分(因此原始代码中的反斜杠)并担心其他 shell 元字符(如果节目名称中有撇号怎么办? , 例如)。

但是,您可以将整个$(...)引号括起来,然后一切正常——我本来想说“如预期的那样”,但实际上并没有如我预期的那样。但是对两者进行测试bashksh表明这有效:

for file in *
do
    mv "$file" "$(echo "$file" | awk '{print $1 " - S0" $3 "E" $5 ".avi"}')"
done

外面的双引号只是简单地防止shell扩展 的输出,$(...)使用$(...)大大简化了生活。简单地用$(...)反引号替换会产生各种问题;在反引号内,您必须逃避各种事情。但似乎符号$(中的$(...)暂停了当前的双引号字符串搜索,并开始搜索以)对中间内容的“正常”解释的结束,这给出了所需的结果。

我认为两行(赋值加使用)更容易理解,但是这个版本在双引号内有复杂的命令替换也可以。


这是一些测试代码,假设您当前没有在当前目录中调用spaced-out的目录(并且您在当前目录中确实具有写权限):

mkdir spaced-out
(
cd spaced-out
# Create files with spaces in the names
for file in "part1 x part2 y part3" "show1 a show2 b show3" \
            "  leader1  x  leader2  y  leader3  "
do
    echo hello world > "$file"
done
ls
ls -l
# Rename the files
for file in *
do
    mv "$file" "$(echo "$file" | awk '{print $1 " - S0" $3 "E" $5 ".avi"}')"
done
# Show results and clean up
ls -l
rm -f *
)
rmdir spaced-out

当作为脚本 ( bash rename.script) 运行时,它会生成:

  leader1  x  leader2  y  leader3   part1 x part2 y part3           show1 a show2 b show3
total 24
-rw-r--r--  1 jleffler  eng  12 Jul  6 08:02   leader1  x  leader2  y  leader3  
-rw-r--r--  1 jleffler  eng  12 Jul  6 08:02 part1 x part2 y part3
-rw-r--r--  1 jleffler  eng  12 Jul  6 08:02 show1 a show2 b show3
total 24
-rw-r--r--  1 jleffler  eng  12 Jul  6 08:02 leader1 - S0leader2Eleader3.avi
-rw-r--r--  1 jleffler  eng  12 Jul  6 08:02 part1 - S0part2Epart3.avi
-rw-r--r--  1 jleffler  eng  12 Jul  6 08:02 show1 - S0show2Eshow3.avi

当运行 as 时bash -x rename.script,它会生成:

+ mkdir spaced-out
+ cd spaced-out
+ for file in '"part1 x part2 y part3"' '"show1 a show2 b show3"' '"  leader1  x  leader2  y  leader3  "'
+ echo hello world
+ for file in '"part1 x part2 y part3"' '"show1 a show2 b show3"' '"  leader1  x  leader2  y  leader3  "'
+ echo hello world
+ for file in '"part1 x part2 y part3"' '"show1 a show2 b show3"' '"  leader1  x  leader2  y  leader3  "'
+ echo hello world
+ ls
  leader1  x  leader2  y  leader3   part1 x part2 y part3           show1 a show2 b show3
+ ls -l
total 24
-rw-r--r--  1 jleffler  eng  12 Jul  6 08:03   leader1  x  leader2  y  leader3  
-rw-r--r--  1 jleffler  eng  12 Jul  6 08:03 part1 x part2 y part3
-rw-r--r--  1 jleffler  eng  12 Jul  6 08:03 show1 a show2 b show3
+ for file in '*'
++ echo '  leader1  x  leader2  y  leader3  '
++ awk '{print $1 " - S0" $3 "E" $5 ".avi"}'
+ mv '  leader1  x  leader2  y  leader3  ' 'leader1 - S0leader2Eleader3.avi'
+ for file in '*'
++ echo 'part1 x part2 y part3'
++ awk '{print $1 " - S0" $3 "E" $5 ".avi"}'
+ mv 'part1 x part2 y part3' 'part1 - S0part2Epart3.avi'
+ for file in '*'
++ echo 'show1 a show2 b show3'
++ awk '{print $1 " - S0" $3 "E" $5 ".avi"}'
+ mv 'show1 a show2 b show3' 'show1 - S0show2Eshow3.avi'
+ ls -l
total 24
-rw-r--r--  1 jleffler  eng  12 Jul  6 08:03 leader1 - S0leader2Eleader3.avi
-rw-r--r--  1 jleffler  eng  12 Jul  6 08:03 part1 - S0part2Epart3.avi
-rw-r--r--  1 jleffler  eng  12 Jul  6 08:03 show1 - S0show2Eshow3.avi
+ rm -f 'leader1 - S0leader2Eleader3.avi' 'part1 - S0part2Epart3.avi' 'show1 - S0show2Eshow3.avi'
+ rmdir spaced-out

双倍行距文件名(可选地带有前导和/或尾随空格)对于声称使用包含空格的文件名的脚本通常是一个非常好的测试。此代码不管理包含换行符的文件名;该awk脚本将一个名称视为两行输入,这不是我们想要的。可以固定;除非你需要你的脚本绝对防弹(例如,因为它是给客户的——客户做最糟糕的事情,而且文件名中带有换行符的文件名在他们的能力范围内,否则它通常不值得麻烦,即使你永远不会自己做)。

于 2013-07-06T15:08:07.113 回答
1

尝试这个:

for file in *
do
   set -- $file
   mv -- "$i" "$(printf "%s - %s %sE%s.avi" "$1" "$file" "$3" "$5")"
done

虽然这是一个猜测 - 您需要显示您的输入和输出文件名的实际外观,而不仅仅是一个无法转换它们的命令!

于 2013-07-06T12:20:57.080 回答
0

好吧,经过更多的挖掘和玩弄之后,我发现我可以将 mv 命令的整个目标包含在引号中,然后删除反斜杠。

但后来我想出了一个不同的问题。这是说 mv 命令的目标不是目录。我知道它不是一个目录...我正在尝试重命名这里的一些文件...(抱怨,抱怨)

mv: target 'Showname - S01E01.avi' is not a directory

无论如何,经过更多谷歌搜索后,我在LinuxQuestions 帖子上发现这显然是由于文件名中的空格造成的。我可以通过将以下内容添加到脚本的开头来解决此问题。

IFS='
'

现在它将我的文件重命名为一个体面的命名方案,以便其他程序/服务可以获取它们并正确添加它们

于 2013-07-06T06:06:34.480 回答