私の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
実際にしたい場合は削除してくださいecho
。echo 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スクリプトをこのようなパイプラインにインラインで含める方法は不明です。 (これは可能ですが、非常に面倒です。)実際の移動を実行するのではなく、目的の宛先ディレクトリを作成するために必要な計算のみを実行することに注意してください。またはを使用すると、ファイルシステム操作も実行できます。python
python
awk
perl
python
いくつかの仮定:
フルネームにファイルを移動しようとしています。元の数値プレフィックスを削除するためにスクリプトを変更することは難しくありません(もちろん、ファイルはすべてで終わらない方が良いですが
_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
分割時に残りを計算します。など)N
M
5 % 3 == 2
6 % 3 == 0
最後に、
printf
パイプラインの次のステップに必要な出力ワイヤを組み立てます。スペースで区切られた2つのフィールドを含む行を作成します。 1つ目は、base
エクスポートディレクトリ名の下限と上限を使用して作成されたターゲットディレクトリの名前です。ここでは、1ベースの出力計算スキームに戻ります。 2番目のフィールドは、再構成された元の入力ファイル名です。
... | while read destdir orig
do
mkdir -p $destdir && mv $orig $destdir
done
これは、すべての操作が実際に完了するパイプラインの最後のステップです。スクリプトによって生成された各行をawk
2つのフィールドに読み込み、
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