如何从包含 80 GB 文件的文件夹中移动 30 GB 的文件?

如何从包含 80 GB 文件的文件夹中移动 30 GB 的文件?

我有一个文件夹,里面有 500 多个视频,文件夹大小约为 80 GB。

我想将 30GB 的视频移动到另一个文件夹,但是该怎么做呢?

我正在使用终端,无法安装 GUI。

答案1

不加选择地(自动地

我不知道有哪个命令行工具可以做到这一点...但是,您可以使用脚本移动文件,直到移动了一定的总大小,例如像这样(只运行一次脚本...如果再次运行它,它将再次从剩余文件中移动指定的大小):

#!/bin/bash

sourc_dir="/path/to/directory" # Set the path to the source directory "no trailing /"
dest_dir="/path/to/directory" # Set the path to the destination directory "no trailing /"
move_size="30" # Set the total size of files to move in Gigabytes
 
# Don't edit below this line
move_size=$(( move_size * 1000000 ))
current_size=$(du -sk "$sourc_dir" | awk '{printf "%d", $1}')
remaining_size=$(( current_size - move_size ))

for f in "$sourc_dir"/*; do
  [ -f "$f" ] || continue
  new_size=$(du -sk "$sourc_dir" | awk '{printf "%d", $1}')
  (( new_size <= remaining_size )) && exit
  mv -n -- "$f" "$dest_dir"/
  done

甚至一行(无需脚本文件)您可以在终端中运行,例如:

for f in /path/to/source/*; do [ -f "$f" ] || continue; (( size >= (30*(1000**3)) )) && break || (( size += $(stat --printf "%s" "$f") ))  && mv -n -- "$f" /path/to/destination/; done

您可能希望unset size能够在同一个 shell 实例中重新运行它。

选择性地(手动

您可以随时根据文件名、大小、类型等从搜索工具中指定 shell 模式或管道。

或者,更方便的是,您可以使用基于控制台的文件管理器,该管理器支持多个文件选择,例如众所周知且功能丰富的GNU 午夜指挥官可从 Ubuntu 官方存储库获取并按如下方式安装:

sudo apt install mc

然后输入mc并点击来运行它Enter...然后您将看到一个可导航的用户界面,您可以在其中使用Insert键盘键选择多个文件...您将能够实时看到所选文件的总大小及其数量,如下所示:

在此处输入图片描述

选择完要移动的所有文件后,按F6键盘键打开移动/重命名对话框并输入目标目录,然后选择[< OK >]一次移动选定的文件,如下所示:

在此处输入图片描述

或者甚至不太知名、功能较少fff(超快的文件管理器:-) ... 这写得很有趣bash... 虽然它支持多个文件选择,但您可能需要稍微定制一下才能看到选定文件的总大小,例如作为快速补丁,您可以从此更改159源文件中的行:fff

local mark_ui="[${#marked_files[@]}] selected (${file_program[*]}) [p] ->"

更改为:

local mark_ui="[${#marked_files[@]}] $(for f in "${marked_files[@]}"; do (( tsize += $(stat --printf "%s" "$f") )); done; tsize=$((tsize/(1000**2))); printf '[%d M]' "$tsize";) selected (${file_program[*]}) [p] ->"

然后您可以像这样简单地运行该文件(无需安装):

bash fff

或者查看使用帮助如下:

bash fff -h

然后,您要做的就是导航到源目录并开始选择要移动的文件m小写) 键盘按键如下:

在此处输入图片描述

选择完文件后,导航到目标目录,然后通过点击p(小写)键盘键。

答案2

这是替代方案拉法的想法是不需要du对整个目录的每个文件进行。相反,它对stat每个文件进行一次,使用find-printf %s获取大小并%p获取要移动的文件的路径。

#!/bin/bash

src='foo'
dest='bar'

# 30GB in bytes to move:
(( bytestomove=30*(1000**3) ))
movedbytes=0

while IFS=$'\t' read -r -d '' bytes file
do
    # if we already moved enough, break out
    (( movedbytes >= bytestomove )) && break;

    # try to move it, break out if it fails
    mv -n -- "$file" "$dest" || break

    # successfully moved, add bytes to movedbytes
    (( movedbytes += bytes ))
done < <(find "$src" -type f -printf "%s\t%p\0")

(( movedmegs=movedbytes/(1000**2) ))
printf "Moved %d MB\n" $movedmegs

答案3

@TedLyngmo 指出,我首先提出的 tar 解决方案有效地复制了文件,但并未移动它们。这种区别很重要,原因有二:

  1. 复制后,源文件仍保留,而移动后,源文件不再存在于其源位置。
  2. 如果文件在同一个文件系统内移动,这个问题也可能出现这种情况,则mv只需将源文件重新链接到新位置,而不复制内容。这通常比复制要快得多,尤其是对于大文件。

移动场景

我们仍然可以使用tar最初给出的复制场景中的概念来构建仅包含找到的前 30GB 文件的文件列表tar,然后仅移动这些文件:

#!/bin/bash

# Convert human-readable number, e.g. 1M, 30G to number of 1024 byte blocks 
limit=$(numfmt --from=auto $1 --to-unit 1024)
src_dir=$2
dest_dir=$3

mkdir $dest_dir
filelist=$(readlink -f $(mktemp -p $dest_dir))

# create a list of files to be moved.  tar -v outputs filenames to stdout, and 
# the dest archive is /dev/null
tar -v -L $limit -F false -c -f /dev/null -C $src_dir . > $filelist 2> /dev/null
tar_result=$?
if [ "$tar_result" = 2 ]; then
    echo "$1 limit was hit - some unmoved files will remain in $dest_dir"
else
    echo "All files will be moved"
fi

pushd $dest_dir > /dev/null
abs_dest_dir=$PWD
# create directories in the destination
grep '/.*/$' $filelist | xargs mkdir -p
popd > /dev/null

pushd $src_dir > /dev/null
# create hard links in the destination
grep -v '/$' $filelist | xargs cp -l --parents -t $abs_dest_dir
# unlink source files
grep -v '/$' $filelist | xargs rm
# remove source directories in reverse order, if they are empty
grep '/.*/$' $filelist | tac | xargs rmdir --ignore-fail-on-non-empty
popd > /dev/null

rm $filelist

这比我想要的更复杂:

  • mv无法对多个文件的源路径和目标路径进行太多控制(特别是没有类似cp--parents 选项),因此文件移动是通过在目标位置创建硬链接,然后取消链接源文件来实现的

  • 由于源目录包含要移动和不移动的条目,因此必须在目标中重新创建目录,而不是简单地移动(连同所有内容)。因此,在移动文件之前会创建相应的目标目录,如果相应的源目录为空,则会在移动文件之后删除它们。

注意事项

  • tar -v每行输出一个文件条目。如果路径名中有特殊字符(例如换行符),则此操作将失败。希望源路径名的命名合理。

  • 这将移动少于 30GB 的实际文件数据,因为流包含元数据(文件名、时间戳等)以及实际内容,并且都受 30GB 限制。

  • 我在这里对 KB 和 kb 的关系有点不拘一格。由于该-L参数以 1024 字节为单位,因此这实际上将移动限制为 3,072,000,000 字节的 tar 流。请根据需要调整数字。

  • 在当前目录中创建了 filelist.txt。此操作需要写入权限,这可能是理所当然的,因为移动操作无论如何都需要写入权限。

复制场景

您可以使用tar将文件夹转换为流,然后tar再次将流重新转换为新位置的文件。第一个tar用法-L将“磁带长度”限制为 3000000 KB,然后再请求新的磁带。 tar使用 -F 在每个“磁带”之后运行false。由于false返回错误,传输在第一个“磁带”之后停止,即 30GB。让所有数据仅经过 2 次 tar 调用应该比使用 bash 循环遍历所有文件更快。

mkdir dest.dir
tar -cv -L 3000000 -F false -C src.dir . | tar -x -C dest.dir/

这将复制文件直到达到 30GB 的限制,然后tar会出现错误:

tar: Unexpected EOF in archive
tar: Unexpected EOF in archive
tar: Error is not recoverable: exiting now
tar: ‘false’ command failed
tar: Error is not recoverable: exiting now

tar将不会传输达到限制时正在传输的文件 - 它根本不会被写入目的地。

注意事项

  • 这将复制少于 30GB 的实际文件数据,因为流包含元数据(文件名、时间戳等)以及实际内容,并且都受 30GB 限制。

  • 我在这里对 KB 和 kb 的关系有点不拘一格。由于该-L参数以 1024 字节为单位,因此这实际上将传输限制为 3,072,000,000 字节的 tar 流。请根据需要调整数字。


此答案的先前编辑使用了 find-cpio-head-cpio,然后是 tar-head-tar。现在提供的 tar-tar 解决方案应该足够了,但如果对其他解决方案感兴趣,请检查编辑历史记录。

答案4

转到文件所在的文件夹:

cd SOURCE_FOLDER

列出该文件夹中的文件列表

ls > list.txt

在任何编辑器中编辑它并删除您不想移动的文件。保存结果,这样 list.txt 现在只包含您要移动的文件。现在运行这个简单的脚本,它从 list.txt 文件中读取每一行并将该文件移动到您的目标目录。

while IFS= read -r line; do mv "$line" ../DESTINATION_DIRECTORY/"$line"; done < list.txt

相关内容