解決“mv:參數列表太長”?

解決“mv:參數列表太長”?

我有一個包含超過一百萬個文件的資料夾需要排序,但我實際上無能為力,因為mv一直輸出此訊息

-bash: /bin/mv: Argument list too long

我使用此命令來移動無擴展名檔案:

mv -- !(*.jpg|*.png|*.bmp) targetdir/

答案1

xargs是完成這項工作的工具。那個,或者find-exec … {} +。這些工具多次運行一個命令,並一次傳遞盡可能多的參數。

當變數參數列表位於末尾時,這兩種方法都更容易執行,但這裡的情況並非如此:最後一個參數mv是目的地。對於 GNU 實用程式(即在非嵌入式 Linux 或 Cygwin 上),-t選項mv很有用,可以先傳遞目標。

如果檔案名稱沒有空格,也沒有任何空格\"',並且不以-1 開頭,那麼您可以簡單地提供檔案名稱作為輸入xargs(該echo命令是 bash 內建命令,因此不受命令列長度限制;如果您看到!: event not found,則需要使用shopt -s extglob) 啟用通配語法:

echo !(*.jpg|*.png|*.bmp) | xargs mv -t targetdir --

您可以使用該-0選項來xargs使用空分隔輸入而不是預設的帶引號的格式。

printf '%s\0' !(*.jpg|*.png|*.bmp) | xargs -0 mv -t targetdir --

或者,您可以使用find.為了避免遞歸到子目錄,請使用-type d -prune.由於沒有為列出的影像檔案指定任何操作,因此僅移動其他檔案。

find . -name . -o -type d -prune -o \
       -name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
       -exec mv -t targetdir/ {} +

(這包括點文件,與 shell 通配符方法不同。)

如果您沒有 GNU 實用程序,則可以使用中間 shell 以正確的順序取得參數。此方法適用於所有 POSIX 系統。

find . -name . -o -type d -prune -o \
       -name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
       -exec sh -c 'mv "$@" "$0"' targetdir/ {} +

在 zsh 中,您可以載入mv內建:

setopt extended_glob
zmodload zsh/files
mv -- ^*.(jpg|png|bmp) targetdir/

或者如果您希望讓mv和其他名稱繼續引用外部命令:

setopt extended_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- ^*.(jpg|png|bmp) targetdir/

或使用 ksh 風格的 glob:

setopt ksh_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- !(*.jpg|*.png|*.bmp) targetdir/

或者,使用 GNUmvzargs:

autoload -U zargs
setopt extended_glob
zargs -- ./^*.(jpg|png|bmp) -- mv -t targetdir/ --

¹ 對於某些xargs實現,檔案名稱也必須是目前區域設定中的有效文字。有些人也會考慮命名_為指示輸入結束的檔案(可以使用 來避免-E ''

答案2

如果使用 Linux 核心就足夠了,你可以簡單地這樣做

ulimit -S -s unlimited

這是可行的,因為 Linux 核心在大約 10 年前包含了一個補丁,該補丁將參數限制更改為基於堆疊大小:https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b6a2fea39318e43fee84fa7b0b90d68bed92d2ba

如果你不想要無限的堆疊空間,你可以說例如

ulimit -S -s 100000

將堆疊限制為 100MB。請注意,您需要將堆疊空間設定為正常堆疊使用量(通常為 8 MB)加上您想要使用的命令列的大小。

您可以透過以下方式查詢實際限額:

getconf ARG_MAX

這將輸出最大命令行長度(以位元組為單位)。例如,Ubuntu 預設為2097152大約 2 MB。如果我以無限堆疊運行,我會得到4611686018427387903正好是 2^62 或大約 46000 TB。如果您的命令列超過,我希望您能夠自己解決該問題。

請注意,如果您在運行中使用sudoas 則無法解決該問題,因為在真正執行之前會重置堆疊大小。要解決這個問題,您必須使用 啟動 root shell ,然後執行,最後在 root shell 中不執行該指令。sudo mv *.dat somewhere/.ulimitsudomvsudo -sulimit -S -s unlimitedsudo

答案3

有時最簡單的方法是寫一個小腳本,例如用 Python:

import glob, shutil

for i in glob.glob('*.jpg'):
  shutil.move(i, 'new_dir/' + i)

答案4

作業系統的參數傳遞限制不適用於 shell 解釋器內發生的擴充。因此,除了使用xargsor之外find,我們還可以簡單地使用 shell 循環將處理分解為單獨的mv命令:

for x in *; do case "$x" in *.jpg|*.png|*.bmp) ;; *) mv -- "$x" target ;; esac ; done

這僅使用 POSIX Shell 命令語言功能和實用程式。這段文字透過縮排更加清晰,並刪除了不必要的分號:

for x in *; do
  case "$x" in
    *.jpg|*.png|*.bmp) 
       ;; # nothing
    *) # catch-all case
       mv -- "$x" target
       ;;
  esac
done

相關內容