
N個のファイルを含むディレクトリを考えてみましょう。
ファイルをアルファベット順にリストし、次のようにリストを保存できます。
ls > list
その後、同じディレクトリにn個のサブディレクトリを作成したいと思います。
mkdir direc{1..n}
list
それでは、最初のm個または最初の5個(1-5)個のファイルをからに移動し、次のdirec1
5個のファイル(例えば6-10)をに移動したいと思いますdirec2
。
これはあなたに些細なことのように思えるかもしれませんが、私は現在それをすることはできません。助けてください。
答え1
list=(*) # an array containing all the current file and subdir names
nd=5 # number of new directories to create
((nf = (${#list[@]} / nd) + 1)) # number of files to move to each dir
# add 1 to deal with shell's integer arithmetic
# brace expansion doesn't work with variables due to order of expansions
echo mkdir $(printf "direc%d " $(seq $nd))
# move the files in chunks
# this is an exercise in arithmetic to get the right indices into the array
for (( i=1; i<=nd; i++ )); do
echo mv "${list[@]:(i-1)*nf:nf}" "direc$i"
done
テスト後、「echo」コマンドをすべて削除します。
または、各ディレクトリに固定数のファイルを含めるには、次の方が簡単です。
list=(*)
nf=10
for ((d=1, i=0; i < ${#list[@]}; d++, i+=nf)); do
echo mkdir "direc$d"
echo mv "${list[@]:i:nf}" "direc$d"
done
答え2
#!/bin/sh
d=1 # index of the directory
f=0 # number of files already copied into direc$d
for x in *; do # iterate over the files in the current directory
# Create the target directory if this is the first file to be copied there
if [ $f -eq 0 ]; then mkdir "direc$d"; fi
# Move the file
mv -- "$x" "direc$d"
f=$((f+1))
# If we've moved 5 files, go on to the next directory
if [ $f -eq 5 ]; then
f=0 d=$((d+1))
fi
done
便利な参考資料:
- 算術拡張
$((…))
- 条件式
[ … ]
- スペースやその他の特殊文字が原因でシェルスクリプトが停止するのはなぜですか?なぜ
--
引用mv -- "$x" …
答え3
d=0; set ./somedirname #init dir counter; name
for f in ./* #glob current dir
do [ -f "$f" ] && set "$f" "$@" && #if "$f" is file and...
[ "$d" -lt "$((d+=$#>5))" ] || continue #d<(d+($#>5)) or continue
mkdir "$6$d" && mv "$@$d" || ! break #mk tgtdir and mv 5 files or
shift 5 #break with error
done
上記のコマンドは、文字列をヘッドとテールにリンクするシェルのarg配列機能を利用します。たとえば、関数を書く場合:
fn(){ printf '<%s>\n' "42$@"; }
...そして次のように呼んでください。
fn show me
...次のように印刷されます。
<42show>
<me>
...最初または最後の要素に追加または追加できるからです。(それぞれ)arg配列の辞書/追加文字列の周りに引用符を入れます。
arg 配列はカウンターとしても機能するので、ここで二重ミッションを実行します。$#
シェル引数は常にこれまで積み重ねられた要素の数を正確に伝えます。
しかし...ステップバイステップの方法は次のとおりです。
d=0; set ./somedirname
$d
新しいディレクトリが作成されるたびに、varは1ずつ増加します。ここではゼロに初期化されます。./somedirname
あなたの好きな方法です。しかし、接頭辞は重要です./
。すべてのタスクを現在のディレクトリにルート化するだけでなく、必要なすべての種類の名前を指定することもできます。(新しい行を使用したりハイフンで始めたい場合は、安全にそれを行うことができますが、おそらく望ましくないでしょう)。 argnameは常に./
noコマンドで始まるため、コマンドラインからオプションとして誤って解釈することはできません。
for f in ./*
- これはループを開始します(そうであれば)
*
現在のディレクトリで一致します。同様に、各一致にはプレフィックスが付きます./
。
- これはループを開始します(そうであれば)
[ -f "$f" ]
- 各繰り返しの一致が確かに一般的なファイルであることを確認してください。(またはリンク)そして…
set "$f" "$@"
- 一致するアイテムをシェル配列に重ね合わせます。これは
./somedirname
常に配列の終わりにあります。
- 一致するアイテムをシェル配列に重ね合わせます。これは
[ "$d" -lt "$((d+=$#>5))" ]
$d
配列に5つ以上の要素がある場合は、1を追加して"$@"
増分結果をテストします。
|| continue
- 2 つのコマンドのいずれかが
[ -f "$f" ]
set ...
[ "$d" -lt...
true を返さない場合、ループは次の反復に続き、ループの残りの部分を完了しようとしません。これは効率的ですそして安全。
- 2 つのコマンドのいずれかが
mkdir "$6$d"
- なぜなら、このセクションは私たちが今であり、値が1だけ増加した場合にのみこのポイントに到達できることを保証するからです
continue
。したがって、一致して移動された5つのファイルの最初のセットに対して5番目のファイルに対してandというディレクトリが作成されます。重要なのは、このコマンドは$# > 5
./somedirname
$6
$d
./somedirname1
./somedirname5
失敗する宛先パス名を持つパス名がすでに存在する場合。つまり、このコマンドはただ同じ名前のディレクトリがない場合は成功します。
- なぜなら、このセクションは私たちが今であり、値が1だけ増加した場合にのみこのポイントに到達できることを保証するからです
mv "$@$d"
$d
これにより、配列が展開され、最後の要素(ターゲットディレクトリ名)の末尾に値が追加されます。したがって、次のように拡張されます。
mv ./file5 ./file4 ./file3 ./file2 ./file1 ./somedirname1
- ...まさにあなたが望むことです。
|| ! break
何らかの理由で最初の 2 つのコマンドのいずれかが正常に完了しなかった場合、
for
ループは停止break
します。送信された戻り値!
のブール逆数break
(通常は0)なので、break
1が返されます。これにより、前のいずれかのコマンドでエラーが発生した場合、ループはfalseを返します。これが重要です。for
ループはwhile/until
ループとは異なりテストするのではなく、繰り返すことです。これら2つのコマンドの戻り値を明示的にテストしないと、シェルがエラーのため必ずしも停止するわけではなく、set -e
親シェルが完全にシャットダウンする可能性があります。代わりに、これは意味のある戻り値を保証し、問題が発生してもループは繰り返されません。一見すると、これはここで止まる唯一の答えのようです。たとえば、
mkdir ./somedirname
trueが返されない場合、他のすべての答えはループを続行します。(エラーが繰り返されるか、または悪いことに、現在のディレクトリのファイルを既存のディレクトリに移動して、同じ名前の別のファイルを上書きする可能性があります。)。ループに任意のファイル名を使用する場合は、次のことを行う必要があります。いつも両方のソースファイルが存在するかどうかをテストするそして目標が存在するからだ。
shift 5
- これは
shift
シェル引数配列の最初の5つの要素を削除します。これを再度挿入し、次の反復のために配列の状態をリセットします./somedirname
。$1
- これは
答え4
このawkプログラムを使用すると、シェルコマンドを生成でき、疑わしい場合は、そのコマンドが正しいことを事前に確認できます。
awk -v n=5 '{ printf "mv \"%s\" %s\n", $0, "direc" int((NR-1)/n)+1 }' list
出力パイプが大丈夫なら、完全なコマンドをsh
。
ls | awk -v n=5 '{ printf "mv \"%s\" %s\n", $0, "direc" int((NR-1)/n)+1 }' | sh
n = 5設定を変更すると、5以外の値を定義できます。
ターゲットディレクトリを動的に作成する場合、バリアントは次のとおりです。
ls | awk -v n=5 '
NR%n==1 { ++c ; dir="direc"c ; print "mkdir "dir }
{ printf "mv \"%s\" %s\n", $0, dir }
' | sh