バッチ処理のためにいくつかのコマンドに引数として渡すことができるファイルの数を計算する方法は?

バッチ処理のためにいくつかのコマンドに引数として渡すことができるファイルの数を計算する方法は?

たとえば、私のディレクトリには、次のように生成された複数のファイルが含まれています。

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

したがって、質問:許容される最大長はどのように計算されますか?


私はいくつかのことをしました:

  1. 全長は以下で確認できます。

     $ getconf ARG_MAX
     2097152
    
  2. 環境変数もパラメータサイズに影響を与えるため、次の値を除く必要がありますARG_MAX

     $ env | wc -c
     3403
    
  3. 正しい値を見つける前に、異なる数のファイルを試して同じサイズの最大ファイル数を決定する方法(バイナリ検索を使用)が開発されました。

     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. 書いたプログラムは、渡されたパラメーターの合計サイズを計算します。このプログラムの結果は似ていますが、計算されていないバイトが残ります。

     $ ./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制限から離れています。

ベストアンサー1

xargsに計算をさせます。

printf '%s\0' files/* | xargs -0 mv -t new_files_dir

おすすめ記事