“覆盖”的文件,空间仍然被占用,它们丢失了吗?

“覆盖”的文件,空间仍然被占用,它们丢失了吗?

所以愚蠢而没有耐心的我在 19.04 服务器上使用了以下脚本,试图将一堆视频文件移动到带有前缀的文件夹中:

dirs=(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
shopt -s nocasematch

for file in *
do
    for dir in "${dirs[@]}"
    do

     if [ -d "$file" ]; then
      echo 'this is a dir, skipping'
      break
     else
      if [[ $file =~ ^[$dir] ]]; then
       echo "----> $file moves into -> $dir <----"
       mv "$file" "$dir"
       break
      fi
     fi
  done
done

不知道哪里出了问题,但是它没有将文件移动到文件夹,而是移动到了单一的输出...所以:

----> a1.ts moves into -> A <----
----> a2.ts moves into -> A <----
----> a3.ts moves into -> A <----
----> a4.ts moves into -> A <----
----> a5.ts moves into -> A <----
----> c1.ts moves into -> C <----
----> c2.ts moves into -> C <----
----> c3.ts moves into -> C <----
----> c4.ts moves into -> C <----
----> c5.ts moves into -> C <----

幸运的是,当我注意到它没有按预期进行时,我立即停止了该过程(CTRL + C),并且没有浏览整个文件夹。

所以现在我得到了这些文件AC,它们的大小不到 1Gb,而且看起来它们是一个单独的视频。

文件夹本身的总磁盘使用量中有 50Gb 未被占用,但计算机的整体磁盘空间保持不变。这让我认为文件没有被删除?

任何帮助表示感谢,谢谢:)

编辑:文件实际上已经消失了,只有最后写入的文件保留了下来,只需要花一些时间来更新磁盘使用信息..故事的寓意是,先在模拟文件上运行你的脚本!

答案1

我认为这是问题所在:您应该创建目录 A、B、C...Z。如果您这样做了,命令mv应该将文件移动到这些目录中。

但如果不是,mv命令会将文件移动到具有这些名称的文件中,A,B,C......我认为这就是你所做的。

为了使 shellscript 更安全,您应该在开始移动之前让它创建目录(如果它们尚不存在)。

dirs=(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)

for dir in "${dirs[@]}"
do
 mkdir -p $dir
done

如果你希望事情变得更加安全,你也可以使用mv选项-i

   -i, --interactive
          prompt before overwrite

答案2

@Sudodus 已经解释过了出了什么问题,但这是下次使用的更简单的脚本版本:

for letter in {a..z}; do 
    dir=${letter^}
    mkdir -p -- "$dir" 
    mv -- "$letter"* "${letter^^}"* "$dir"/
done

解释

  • for letter in {a..z}; do:扩展为和{a..z}之间的所有小写字母:az

    $ echo {a..z}
    a b c d e f g h i j k l m n o p q r s t u v w x y z
    

    因此这将遍历所有小写字母,并将每个字母保存为$letter

  • dir=${letter^}:语法${var^^}返回变量的内容$var,其中第一个字符为大写(因为这只有字符,这就是我们所需要的)。因此,如果$lettera,则${letter^^}A,因此$dir将是当前 的大写版本$letter

  • mkdir -p -- "$dir":创建目录。如果目录已存在,则不执行任何操作(-p)。-- 表示选项的结束,对于防止以 开头的名称很有用-
  • mv -- "$letter"* "${letter^}"* "$dir":将每个文件(或目录)移动到相关目标。

问题在于,它还会移动您可能拥有的任何目录。它不会移动目标目录,因为它们要么尚不存在,要么您会尝试将它们移动到自身中,但任何非目标目录的现有目录都将被移动。

如果这是一个问题,你必须做这样的事情:

for file in *; do 
    if [[ ! -d "$file" ]]; then 
        letter="${file:0:1}"
        dir="${letter^}"
        mkdir -p -- "$dir"
        mv -- "$file" "$dir"/
    fi
done

答案3

您可以根据模式对文件进行匹配,而不必根据字典数组检查每个文件,这会产生很多迭代。

非常基本的排序:

#!/bin/bash

videos=./videos
sorted=./sorted

# sort types link,move.
sort_type=link

find "$videos" -maxdepth 1 -type f \
   \( -name '*.avi' -o -name '*.mkv' -o -name '*.mp4' \) -print0 |

while IFS= read -r -d ''; do

    b=$(basename "$REPLY")
    c=${b::1}

    case $c in
        [a-zA-Z]) label=${c^} ;; [0-9]) label="0-9" ;; *) label="_" ;;
    esac

    [[ ! -d "$sorted/$label" ]] && mkdir -p "$sorted/$label"

    if [[ -L $sorted/$label/$b ]] || [[ -e $sorted/$label/$b ]]; then
        echo "File/link: '$b' exists, skipping."
        continue
    fi

    case $sort_type in
        link)
            ln -rfst "$sorted/$label" -- "$REPLY"
            ;;
        move)
               mv -t "$sorted/$label" -- "$REPLY"
            ;;
    esac
done

答案4

以下是一些阻止mv覆盖现有文件的方法:

  • 如果要移动到目录,请在目标后附加一个斜杠,即使用mv "$file" "$dir"/而不是mv "$file" "$dir"。如果$dir不存在或不是目录,mv则会抱怨:

    $ touch a
    $ mv a z/
    mv: cannot move 'a' to 'z/': Not a directory
    $ touch z
    $ mv a z/
    mv: failed to access 'z/': Not a directory
    

    这似乎使得系统调用rename("a", "z/"),所以如果有人同时处理同一组文件,它应该不会受到检查时到使用时漏洞的影响。

  • 或者,使用。同样,如果它不是目录,mv -t "$dir" "$file"它会抱怨。$dir

  • 使用该-n选项防止覆盖现有文件:

    -n, --no-clobber
        do not overwrite an existing file
    

    它不会阻止重命名第一个文件,但不会将其与其他文件一起丢弃。

    这似乎调用了一个简单的rename(),因此同时处理可能不安全。(有renameat2()它将支持一个标志来防止覆盖。)

相关内容