重命名目录 bash 脚本 - target ...没有这样的文件或目录

重命名目录 bash 脚本 - target ...没有这样的文件或目录

我一直在一个简单的目录结构上对此进行测试。
我正在尝试将名称为“Season”的任何目录和/或子目录更改为“Sn”。我已经到了脚本会更改我想要的内容的地步......除了顶级目录之外,如下面的列表所示 - “第 1 季”“第 2 季”“第 3 季”。

目录结构:

.
├── AnotherShow
│   ├── Sn1
│   ├── Sn2
│   ├── Sn3
│   └── Sn4
├── Movie1
│   ├── Sn1
│   └── Sn2
├── Movie2
│   ├── Sn1
│   ├── Sn2
│   ├── Sn3
│   └── Sn4
├── Movie3
│   ├── Sn1
│   └── Sn3
├── Movie4
│   ├── Sn2
│   └── Sn3
├── Season 1
├── Season 2
├── Season 3
├── Show
│   ├── Sn1
│   ├── Sn2
│   ├── Sn3
│   ├── Sn4
│   └── Sn5
└── TV
    ├── Sn1
    ├── Sn2
    ├── Sn3
    └── Sn4

我的脚本是:

array="$(find . -maxdepth 2 -type d -iname 'Season*' -print)"; # A more refined way to search for Seasons in    a directory.
for dir in "${array[@]}"; do # Put this list into an array.  Surround the array with quotes to keep all space   s (if any) together.
   new="$(echo "$dir" | sed -e 's/Season 0/Sn/' -e 's/Season /Sn/')"; # Only change Season 0 to SN.  Leave othe   rs alone.
   sudo mv -v "$dir" "$new" && echo Changed "$dir" to "$new"; 
done                                                           

答案1

问题是您不是在创建数组,而是在创建字符串。您可以通过尝试打印数组的第一个元素来轻松测试这一点:

$ array="$(find . -maxdepth 2 -type d -iname 'Season*' -print)"; 
$ echo $array
./Season 3 ./Season 1 ./Season 2

看起来不错吧?但如果这是一个数组,您将能够单独打印每个元素。不幸的是,上面是一个简单的字符串1而不是数组:

$ echo ${array[0]}
./Season 3 ./Season 1 ./Season 2
$ echo ${array[1]}

该数组实际上由单个字符串组成,这就是“数组”的第二个元素 ( ${array[1]}) 为空的原因。在任何情况下,您都不需要为此使用数组,只需读取 的输出find(您也不需要-print,这就是它默认执行的操作)。您的脚本的工作版本可能是:

#!/usr/bin/env bash
find . -maxdepth 2 -type d -iname 'Season*' | sort -r |
while IFS= read -r dir
do 
    mv "$dir" ${dir/Season /Sn} && echo Changed "$dir" to "$dir/Season /Sn}"; 
done

这里使用了一些技巧。首先,while循环遍历 的结果find。这与您使用的循环的基本原理相同,for但与之结合read可以从标准输入读取。

需要IFS=避免在空格上分割(没有它,目录Season 1将被分割为Season1)。

of -rread 告诉它不允许反斜杠转义字符(像\t和 之类的东西\n按字面意思处理),这确保它可以处理最奇怪的名称。

sort -r是必需的,这样如果您有一个与另一个匹配目录中的模式匹配的目录,则子目录会列在父目录之前。例如:

./Season 12/Season 13
./Season 12

这可以确保您运行mv "Season 12" "Sn12" 跑步mv "Season 12"/"Season 13" "Season 12"/"Sn13"。如果不这样做,第二个命令将失败,因为Season 12不再存在。

最后,我删除了sudo.您应该将脚本运行为sudo script.sh.sudo在脚本中进行随机调用从来都不是一个好主意。


1我不确定细节,但显然,bash 将字符串变量视为一个元素的数组。

答案2

我还不知道问题是什么,但我对你的脚本有一些评论。

尽量不要在循环中调用二进制文件(带有单个数据)。这会影响性能。

您应该使用 sudo 调用整个脚本,但不要在脚本内调用 sudo。

您不需要sed更改文件名。bash可以自己做:

if [[ $dir =~ "Season 0" ]]; then
  new="${dir/Season 0/Sn}"
else
  new="${dir/Season /Sn}"
fi

或者没有if

new="${dir/Season /Sn}"
new="${new/Sn0/Sn}"

你不需要数组:

find ... | while read dirpath; do

这样做的“优点”是目录名称中的空格不会破坏数组定义。我不知道你为什么array="$(find ... -print)"工作。它不应该(因为 shell 不应该在空格和换行符之间进行单词分割)。

答案3

只是上面的简短变体

for dir in ./*/Season\ */ 
do
  mv "$dir" "${dir/eason /n}" && echo "$dir changed to ${dir/eason /n}"
done

与子目录一起使用季节。如果您想重命名文件(不仅仅是目录),只需/在模式末尾删除,这样它看起来像./*/Season\ *

如果您需要,sudo我会建议您使用一行命令(而不是脚本)来完成此操作

for dir in ./*/Season\ */ ; do sudo mv -v "$dir" "${dir/eason /n}" ; done

相关内容