Linuxでは、ファイル名に基づいて多数のファイルをディレクトリに移動する

Linuxでは、ファイル名に基づいて多数のファイルをディレクトリに移動する

私のLinuxサーバーのディレクトリには、次の名前パターンを持つ多くのファイルがあります。

1_file.txt
2_file.txt
3_file.txt
...
1455728_file.txt

最初の100000個のファイルを移動する方法はありますか(1_file.txt〜100000_file.txt) ディレクトリ入力1_100000、2番目の100000ファイル(100001_file.txt~200000_file.txt) ディレクトリ入力100001_200000、など…?

答え1

未テスト

私は次のことをします:

#!/bin/bash
bottom=0
while [[ $bottom -lt 150000 ]] ; do
    myfirst=$((bottom + 1))
    mylast=$((bottom + 100000))
    bottom=$((bottom + 100000))

    dir="${myfirst}_$mylast"
    [[ -d "$dir" ]] || mkdir "$dir"
    seq $myfirst $mylast | \
        while read p ; do
            q="${p}_file.txt"
            [[ -f "$q" ]] && echo "$q"
        done | \
            xargs --no-run-if-empty  echo mv -t "$dir"

done

実際にしたい場合は削除してくださいechoecho mv

答え2

スクリプトファイル

#!/bin/bash

step=100000
file_dir=$1

# Counting of files in the directory
shopt -s nullglob
file_list=("${file_dir}"/*)
file_num=${#file_list[@]}

# Every file's common part
suffix='_file.txt'

for((from = 1, to = step; from <= file_num; from += step, to += step)); do
    new_dir="${from}_${to}"
    mkdir "${file_dir}/${new_dir}"

    if ((to > file_num)); then
        to="$file_num"
    fi
    
    # Generating filenames by `seq` command and passing them to `xargs`
    seq -f "${file_dir}/%.f${suffix}" "$from" "$to" | xargs mv -t "${file_dir}/${new_dir}"
done

使用法:./script.sh files

テスト

次のコマンドを使用してファイルを生成しました。

printf '%s\0' files/{1..1455728}_file.txt | xargs -0 touch

次に、次のようにします。

$ time ./script.sh files

# Time is:
real    10m43,618s
user    0m9,953s
sys 0m19,671s

とてもゆっくり。

結果

$ ls -1v files
1_100000
100001_200000
200001_300000
300001_400000
400001_500000
500001_600000
600001_700000
700001_800000
800001_900000
900001_1000000
1000001_1100000
1100001_1200000
1200001_1300000
1300001_1400000
1400001_1500000

答え3

シェルで算術演算を実行することは可能ですが、常に厄介なので、ここでほとんどの作業を実行するには、他のスクリプト言語を見つけることをお勧めします。以下で使用されていますが、そのまま使用することもawkできます。以下の例でも簡単に使用できるperlと言いたいのですが、構文の観点から、Pythonスクリプトをこのようなパイプラインにインラインで含める方法は不明です。 (これは可能ですが、非常に面倒です。)実際の移動を実行するのではなく、目的の宛先ディレクトリを作成するために必要な計算のみを実行することに注意してください。またはを使用すると、ファイルシステム操作も実行できます。pythonpythonawkperlpython

いくつかの仮定:

  • フルネームにファイルを移動しようとしています。元の数値プレフィックスを削除するためにスクリプトを変更することは難しくありません(もちろん、ファイルはすべてで終わらない方が良いですが_file.txt)。

  • _ファイル名にはスペースがなく、1 つしかありません。そうでない場合でも、次の操作は引き続き機能しますが、awkスクリプトと後続のシェルループではさらに注意を払う必要があります。

したがって、これを考慮すると、次のことが機能します。

ls | 
awk -F_ '
{
    n = $1 - 1               # working zero based is easier here
    base = n - (n % 100000)  # round down to the nearest multiple of 100,000
    printf "%d_%d %s_%s\n", base + 1, base + 100000, $1, $2
}' |
while read destdir orig
do
    mkdir -p $destdir 
    mv $orig $destdir
done

どうしたの?

ls | ...

これはファイル名だけをリストし、出力はターミナルではなくパイプに送信されるため、1行に1つのファイル名がリストされます。ファイルはlsデフォルトの順序でソートされますが、残りのスクリプトはそれを気にせず、任意のファイル名のリストとしてうまく機能します。

... | awk -F_ '
{
    n = $1 - 1               # working zero based is easier here
    base = n - (n % 100000)  # round down to the nearest multiple of 100,000
    printf "%d_%d %s_%s\n", base + 1, base + 100000, $1, $2
} | ...'

複雑ではありませんが、awk以前にプレイしたことがない場合は、理解するのは少し難しいです。まず、目標は一度に1つのファイル名を読み取りls、各ファイル名に対して2つのフィールドを持つ出力ラインを作成することです。最初のフィールドには元のファイル名の適切な宛先ディレクトリがあり、2番目のフィールドには元のファイル名のファイル名が渡されます。パイプラインの次の部分で使用できるようにします。だからもう少し詳しく言えば、

  • Flagは各入力行を文字フィールドに分割するように指示-F_します。これがファイル名に一度だけ発生すると仮定すると、awkは名前の数字部分も割り当てます。その後、説明したようにサポートブロックが適用され設定されます。awk__$1$2_$1$2

  • 計算により、baseファイルが属する100,000個のファイルブロックが決まります。まずファイル名の初期番号から引いてn計算します。1この数字はゼロから始まるので、次の行で使用されるモジュラー操作をより簡単に使用できます。次に、n最寄りの100,000の倍数に降下します。すでに100,000の倍数の場合はn影響を受けません。 ("%"演算子に慣れていない場合は、N % M分割時に残りを計算します。など)NM5 % 3 == 26 % 3 == 0

  • 最後に、printfパイプラインの次のステップに必要な出力ワイヤを組み立てます。スペースで区切られた2つのフィールドを含む行を作成します。 1つ目は、baseエクスポートディレクトリ名の下限と上限を使用して作成されたターゲットディレクトリの名前です。ここでは、1ベースの出力計算スキームに戻ります。 2番目のフィールドは、再構成された元の入力ファイル名です。

... | while read destdir orig
do
    mkdir -p $destdir && mv $orig $destdir
done

これは、すべての操作が実際に完了するパイプラインの最後のステップです。スクリプトによって生成された各行をawk2つのフィールドに読み込み、

  • mkdir -p(ディレクトリがすでに存在する場合は何もしません)次のようにしてディレクトリが存在することを確認します。
  • 成功すると、ソースファイルを新しいディレクトリに移動します。

mkdir ... && mv ...通常、シェルスクリプトでこのパターンを使用することをお勧めします。mkdir何らかの理由で失敗した場合、名前の変更は試みられないためです。

単純だが便利な方法でデータを徐々に変換するいくつかのパイプラインステップのパターンは、さまざまなシェルスクリプトを書くための非常に効果的な方法です。これにより、プロセスとパイプライン制御でシェルの利点を活用しながら、シェルがうまくいかない複雑な計算をより適切な言語にプッシュできます。

答え4

から適応私の答えあなたのため関連質問:

#! /bin/zsh -

zmodload zsh/files # makes mv and a few other file manipulation commands builtin
batch=10000

highest=(<1->_file.txt(n[-1]))
highest=${highest%%_*}

for ((start = 1; start <= highest; start += batch)); do
  (( end = start + batch - 1))
  files=(<$start-$end>_file.txt(N))
  if (($#files)); then
    mkdir -p ${start}_${end} || exit
    mv -- $files ${start}_${end}/ || exit
  fi
done

関連情報