如何從包含 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每個文件執行 a一次,使用find's-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 位元組為單位工作,因此這實際上限制了 tar 流的移動量為 3,072,000,000 位元組。適當調整數字。

  • filelist.txt 在目前目錄中建立。為此需要寫入權限,這可能是給定的,因為移動操作無論如何都需要寫入權限。

複製場景

您可以使用tar將資料夾轉換為流,然後tar再次將流轉換回新位置中的檔案。第一個tar用途是-L在請求新磁帶之前將“磁帶長度”限制為 3000000 KB。 使用 -F在每個“磁帶”之後tar運行。false由於false回傳錯誤,傳輸在第一個「磁帶」(即 30GB)之後停止。讓所有資料僅透過 2 次 tar 呼叫應該比使用 bash 循環遍歷所有檔案更快。

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

這將複製文件,直到達到 30GB 限制,然後tars 將出錯:

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 位元組為單位,因此這實際上將 tar 流的傳輸限制為 3,072,000,000 位元組。適當調整數字。


此答案的先前編輯使用 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

相關內容