0

我已将 ls 命令的输出通过管道传输到文件中。内容是这样的:

[Chihiro]_Grisaia_no_Kajitsu_-_01_[1920x816_Blu-ray_FLAC][D2B961D6].mkv
[Chihiro]_Grisaia_no_Kajitsu_-_02_[1920x816_Blu-ray_FLAC][38F88A81].mkv
[Chihiro]_Grisaia_no_Kajitsu_-_03_[1920x816_Blu-ray_FLAC][410F74F7].mkv

我尝试根据剧集编号重命名这些剧集如下:

cat grisaia | while read line; 
   #get the episode number
   do EP=$(echo $line | egrep -o  "_([0-9]{2})_" | cut -d "_" -f2)
   if [[ $EP ]]
      #escape special characters
      then line=$(echo $line | sed 's/\[/\\[/g' | sed 's/\]/\\]/g')
      mv "$line" "Grisaia_no_Kajitsu_${EP}.mkv"
   fi
done

mv 命令以代码 1 退出,并出现以下错误:

mv: 无法统计 '\[Chihiro\]_Grisaia_no_Kajitsu_- 01 \[1920x816_Blu-ray_FLAC\]\[D2B961D6\].mkv': 没有这样的文件或目录

我真正不明白的是,如果我复制无法统计的文件并尝试统计该文件,它就可以工作。我什至可以采用与输出完全相同的字符串并单独执行 mv 命令。

4

1 回答 1

3

如果$line用双引号 ( ") 将变量 ( ) 括起来,则不需要转义这些特殊字符。所以你有两个选择:

  1. 完全删除以下分配:

    then # line=$(echo $line | sed 's/\[/\\[/g' | sed 's/\]/\\]/g')`
    

    或者

  2. 删除以下行中的双引号:

    mv $line "Grisaia_no_Kajitsu_${EP}.mkv"
    

进一步的考虑

  1. 解析的输出ls从来都不是一个好主意。考虑带空格的文件名。有关详细信息,请参阅此文档

  2. 这里cat是不必要的:

    cat grisaia | while read line; 
        ...
    done
    

    使用它来避免不必要的管道:

    while read line; 
        ...
    done < grisaia
    

为什么在某些情况下避免使用管道是件好事?(回答评论)

管道创建子外壳(很昂贵),您还可能会犯一些错误,如下所示:

last=""

cat grisaia | while read line; do
    last=$line
done

echo $last # surprise!! it outputs an empty string

原因是$last循环内部属于另一个子shell。

现在,看看没有管道的相同方法:

while read line; do
    last=$line
done < grisaia

echo $last # it works as expected and prints the last line
于 2017-09-01T02:11:14.637 回答