如何計算可以作為參數傳遞給批處理命令的文件數量?
例如,我有通過這種方式創建的多個文件的目錄:
touch files/{1..10231}_file.txt
我想將它們移動到新目錄
new_files_dir
中。最簡單的方法是:
for filename in files/*; do mv "${filename}" -t "new_files_dir" done
這個腳本在我的電腦上工作了10秒。它很慢。由於
mv
為每個文件執行命令而發生緩慢。###編輯開始###
我已經明白,在我的例子中,最簡單的方法就是
mv files/* -t new_files_dir
或者,如果“參數列表太長”:
printf '%s\0' files/* | xargs -0 mv -t new_files_dir
但上述案例是任務的一部分。整個任務都在這個問題上:根據 linux 中的文件名將大量文件移動到目錄中。因此,必須將文件移動到相應的子目錄中,其對應關係基於文件名中的數字。這是
for
我的程式碼片段中循環使用和其他奇怪的原因。###編輯結束###
可以通過將一堆文件
mv
而不是單個文件傳遞給命令來加速此過程,如下所示:batch_num=1000 # Counting of files in the directory shopt -s nullglob file_list=(files/*) file_num=${#file_list[@]} # Every file's common part suffix='_file.txt' for((from = 1, to = batch_num; from <= file_num; from += batch_num, to += batch_num)); do if ((to > file_num)); then to="$file_num" fi # Generating filenames by `seq` command and passing them to `xargs` seq -f "files/%.f${suffix}" "$from" "$to" | xargs -n "${batch_num}" mv -t "new_files_dir" done
在這種情況下,腳本工作0.2秒。因此,性能提高了 50 倍。
**但是有一個問題:**在任何時候程序都可以由於“參數列表太長”而拒絕工作,因為我不能保證一堆文件名的長度小於最大允許長度。
我的想法是計算
batch_num
:batch_num = "max allowable length" / "longest filename length"
然後
batch_num
在xargs
.因此,**問題是:**如何計算最大允許長度?
我做了一些事情:
- 總長度可以通過這種方式找到:
$ getconf ARG_MAX 2097152
- 環境變數也會影響參數大小,因此可能應該從 中減去它們
ARG_MAX
:$ env | wc -c 3403
- 通過在找到正確值之前嘗試不同數量的文件來確定相同大小文件的最大數量的方法(使用二進制搜尋)。
function find_max_file_number { right=2000000 left=1 name=$1 while ((left < right)); do mid=$(((left + right) / 2)) if /bin/true $(yes "$name" | head -n "$mid") 2>/dev/null; then left=$((mid + 1)) else right=$((mid - 1)) fi done echo "Number of ${#name} byte(s) filenames:" $((mid - 1)) } find_max_file_number A find_max_file_number AA find_max_file_number AAA
輸出:
Number of 1 byte(s) filenames: 209232 Number of 2 byte(s) filenames: 190006 Number of 3 byte(s) filenames: 174248
但我還無法理解這些結果背後的邏輯/關係。 4. 已嘗試使用此答案中的值進行計算,但它們不適合。 5. 編寫了一個C程序來計算傳遞參數的總大小。該程序的結果很接近,但還剩下一些未計數的字節:
$ ./program {1..91442}_file.txt arg strings size: 1360534 number of pointers to strings 91443 argv size: 1360534 + 91443 * 8 = 2092078 envp size: 3935 Overall (argv_size + env_size + sizeof(argc)): 2092078 + 3935 + 4 = 2096017 ARG_MAX: 2097152 ARG_MAX - overall = 1135 # <--- Enough bytes are # left, but no additional # filenames are permitted. $ ./program {1..91443}_file.txt bash: ./program: Argument list too long
程序.c
#include <stdio.h> #include <string.h> #include <unistd.h> int main(int argc, char *argv[], char *envp[]) { size_t chr_ptr_size = sizeof(argv[0]); // The arguments array total size calculation size_t arg_strings_size = 0; size_t str_len = 0; for(int i = 0; i < argc; i++) { str_len = strlen(argv[i]) + 1; arg_strings_size += str_len; // printf("%zu:\t%s\n\n", str_len, argv[i]); } size_t argv_size = arg_strings_size + argc * chr_ptr_size; printf( "arg strings size: %zu\n" "number of pointers to strings %i\n\n" "argv size:\t%zu + %i * %zu = %zu\n", arg_strings_size, argc, arg_strings_size, argc, chr_ptr_size, argv_size ); // The enviroment variables array total size calculation size_t env_size = 0; for (char **env = envp; *env != 0; env++) { char *thisEnv = *env; env_size += strlen(thisEnv) + 1 + sizeof(thisEnv); } printf("envp size:\t%zu\n", env_size); size_t overall = argv_size + env_size + sizeof(argc); printf( "\nOverall (argv_size + env_size + sizeof(argc)):\t" "%zu + %zu + %zu = %zu\n", argv_size, env_size, sizeof(argc), overall); // Find ARG_MAX by system call long arg_max = sysconf(_SC_ARG_MAX); printf("ARG_MAX: %li\n\n", arg_max); printf("ARG_MAX - overall = %li\n", arg_max - (long) overall); return 0; }
我在 StackOverflow 上問過這個程序的正確性問題:argv、envp、argc(命令行參數)的最大匯總大小總是遠離 ARG_MAX 限制。
讓 xargs 為您計算。
printf '%s\0' files/* | xargs -0 mv -t new_files_dir
您的問題似乎假設存在實際的“參數數量限制”,而實際上它是兩個限制的組合:
- 命令行參數和環境變數的字元串長度總和,包括它們的終止 NUL 字節。
- 單個命令行參數的最大字元串長度。
例如,您可以使用 200000 個單字母參數、100000 個雙字母參數呼叫命令,但不能使用超過 128k 字節的單個參數。
假設
xargs
來自 GNU coreutils,xargs --show-limits </dev/null
將顯示這些限制在您的系統上。在任何系統上,在建構命令行時都不會
xargs
使用系統的最大限制,但會選擇一些合理的東西(以這種方式對系統施加壓力是沒有意義的)。