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


11

所以愚蠢的不耐烦,我在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)。

因此,现在我有了小于Gb的那些文件AC,从外观上看,它是一个单视频。

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

任何帮助表示赞赏,谢谢:)

编辑:文件实际上不见了,只有最后一个要写入的文件保留了,只花了一些时间来更新磁盘使用信息。故事的实质是,在模拟文件上运行脚本!


3
也做了目录命名AB并运行该脚本之前等存在?如果不是,您只是重命名了文件。名称以开头aA已重命名为的所有文件A,因此只有最后一个重命名的文件可以保存,其他文件将被覆盖。调用变量dir不会创建目录!
mook765

2
多数民众赞成在解释它的方式。“最后重命名的文件幸存”哈。目录不存在,我应该为每个手工添加一个“接触”。感谢您的澄清
我是TI计算器,

4
+1代表“ ..讲故事的道德,在脚本之前在模拟文件上运行脚本!”
sudodus

4
避免此类问题的提示:使用mv "$file" "$dir/",带有结尾/;如果$dir不存在,mv则会发生错误,而不是将其重命名$file$dir。同时考虑mv -imv -n。并始终mkdir -p在移动前做好准备,以取得良好效果。
marcelm

3
@sudodus更道德的说法是“始终备份您的数据!”。
乔恩·本特利

Answers:


15

我认为这是问题:你应该已经创建目录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

1
将增加touch是一个很好的替补多来mkdir,以避免冲突运行脚本多次的情况下?
我是TI计算器,

2
touch如果名称不存在,则创建一个文件。因此,在这种情况下,它不会执行您想要的操作。mkdir -p可以多次使用该脚本。
sudodus

6
可以使mv安全性更简单的另一种简单方法是,养成在目标是目录时在目标名称上添加斜杠的习惯,即mv "$file" "$dir/"
steeldriver

7

@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}扩展为a和之间的所有小写字母z

    $ 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大写的第一个字符的变量的内容(因为它只有一个字符,这就是我们所需要的)。因此,如果$letteris a,则${letter^^}is A,因此$dir将是current的大写版本$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

${letter^}和之间有什么区别${letter^^},如果相同,为什么要使用${letter^^}代替$dir
基金莫妮卡的诉讼案

1
@NicHartley ${var^}仅将首字母${var^^}大写,而将所有字母大写。这里没有任何区别,因为$letter只有一个字母。
terdon

这是一个完美的答案,除非您可能想通过$dirmv命令中添加目录斜线来增加额外的注意层。(以目前的格式,如果文件以单字母大写字母形式存在,将失败)
Stig Hemmer

@StigHemmer哦,是的。很好,谢谢。答案已编辑。
terdon

4

您可以将文件与模式进行匹配,而不是根据创建大量迭代的字典数组检查每个文件。

非常基本的排序:

#!/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

也许我做错了,但是那肯定没用,丢失了十个文件。我遇到了麻烦,没有充分准备“回复”部分?目的是什么(文件夹ABCD ....内部)仅是别名文件本身指向.. tha别名
我是TI计算器

当未提供任何参数时,将REPLY设置为read内置命令读取的输入行。
bac0n19年

好的,然后最后您执行ln -rfst“ $ sorted / $ label”-“ $ REPLY”如果我们只是使用mv移动了别名,为什么还要使用别名呢?
我是TI计算器,

默认情况下,它将创建从“视频”目录到已排序目录的链接,如果要对其进行MV,则需要取消注释“移动视频”并注释“链接视频”(我认为符号链接不太可怕)
bac0n

...不是两个人都在同一时间
bac0n19年

2

保护您的.bashrc:

alias mv="mv -n --backup=numbered"

1
@Zanna,谢谢你!添加引号。

几个问题,只是要确定(是一个新手),在加入.zshrc文件是有效的太(如果使用ZSH)?在男子MV它说,-n Do not overwrite an existing file. (The -n option overrides any previous -f or -i options.)所以它很重要的是,-n标签将是继标记之前?该编号--backup =将创建一个双重的每一项权利,是不是有点大材小用(和空间的能量/消费)与尤伯杯大型视频文件打交道时(说话的兆兆字节)。谢谢 !
我是TI计算器,

2

记录下来,一些方法可以停止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

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

    这似乎叫作Plain rename(),所以同时进行处理可能并不安全。(renameat2()这将支持一个标志以防止覆盖。)


1

虽然显然不是您想要的情况,但您可以这样做而不丢失文件。这将需要满足以下两个条件之一:

  • 指向相同文件的一个或多个“硬链接”存在于文件系统上的其他位置
  • 一个或多个进程打开了一个文件

Unix文件系统允许多个目录条目引用完全相同的文件内容。这称为“ 硬链接 ”。您可以使用ln命令创建硬链接,而无需使用常规-s(软/符号)选项。只要文件内容至少存在一个硬链接,文件系统就不会重用它。

(请注意,权限通常适用于文件内容,而不适用于目录条目。这就是为什么普通用户有时可以删除拥有的文件root,但不能对其进行写入的原因。删除操作会修改文件夹,而不是文件本身。 )

只要至少一个进程打开了文件,文件系统也不会重用文件内容。即使不存在目录条目,文件系统也不会认为该空间是空闲的,直到没有进程打开它为止。该文件可以恢复从虚拟文件系统/proc/<pid>/fdroot,只要该文件保持打开状态。(感谢@fluffysheap。)


1
如果确实有一个打开文件的进程,则可以通过在/ proc / <pid> / fd中查找来恢复该文件。参见superuser.com/questions/283102/…–
fluffysheap
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.