
私は現在、次のzsh-snippetを使用してさらなる処理のために小さなファイルバッチを選択しています。
for f in $(ls /some/path/*.txt | head -2) ; do
echo unpacking $f
./prepare.sh $f && rm -v $f
done
$(ls ... | head -2)
zshよりも良い選択肢がありますか?
私の使命の一般的な概要です。ニューラルネットワークを訓練するためのデータセットを作成しています。ここでは、機械学習タスクの詳細は重要ではありません。データセット作成操作を実行するには、多数のファイルを手動で処理する必要がありました。そのために、私はそれらを別のディレクトリにコピーしました。次に、いくつかのファイル(この例の出力から最初の2つls
)をランダムに選択し、いくつかの前処理ルーチンを呼び出し、結果を確認し、そのいくつかを生成中のデータセットに移動し、残りを削除します。 。クリーニング後、上記のコマンドを再実行しました。
また、シェルプログラミング技術を向上させ、新しいことを学びたいです:)
これらの「最初の」ファイルが選択される順序は重要ではありません。結局、すべてのファイルが処理されるからです。
言い換えれば、私はfor
ループ内でPCを使用して作業しており、数回の繰り返しの後にPCが一時停止して待機したいと思います。
擬似コード。
for f in /some/path/*.txt ; do
echo unpacking $f
./prepare $f
if human wants to review ; then
human is reviewing then cleans, and PC waits
fi
done
この奇妙なプロセスが発生する理由は、1つ.txt
の「ソース」ファイルを前処理すると数十の異なるファイルが生成され、そのすべてのファイルを見て、ネットワークトレーニングに適したいくつかのサンプル(通常1-2)を選択する必要があるためです。 。
実行できますが、for f in /some/path/*.txt ; do ./prepare $f ; done
このコマンドは何百ものファイルを生成します。これは圧倒的です。
答え1
グローバル予選
Glob修飾子は、ほとんどのファイル使用ls
またはfind
列挙ファイルを置き換えることができます。これはzshのユニークな機能です。
たとえば、(事前の順序でファイルを列挙し、最初の2つのファイルのみを保持)はzshの1$(ls /some/path/*.txt | head -2)
に等しくなります。修飾子は、一致がない場合はリストが空であることを確認し、修飾子は一致を指定された範囲に制限します。/some/path/*.txt(N[1,2])
N
[from,to]
修飾子がない場合、N
デフォルトオプションで一致するファイルがない場合は、エラーメッセージとともにスクリプトが終了します。
o
または、O
修飾子を使用してファイルの順序を制御することもできます。たとえば、/some/path/*.txt(Nom[1,2])
2 つの最新のファイルをインポートします。
1 には通常 zsh に有利なわずかな違いがあります。空白や改行文字、無効なバイトシーケンスなどの特殊文字を含むファイル名で問題が発生する傾向がありますが、ls
zshの組み込み機能はすべてのファイル名で確実に機能します。極端なケースでは、エラー管理が異なります。ここでオプションを忘れたため、-d
これらのファイルの一部が次の種類の場合ls
でも問題が発生します。*.txt
目次ls
内容も一覧表示されます。
しかし、両方のファイルをインポートすることが全体的な目標を達成するのにどのように役立つのかわかりません。すべてのファイルを処理するが、人々が最初の数個のファイルだけを表示できるようにする方法が必要な場合は、ステップ/続行/中断プロンプトを表示できます。このような:
pause=1
for f in /some/path/*.txt ; do
print -ru2 unpacking $f
./prepare $f
if ((pause)); then
print -ru2 -- "$f output is ready for review."
c=
while [[ $c != [anq] ]]; do
read -k1 "c?Process (N)ext, (A)ll, (Q)uit? " && c=${c:l}
done
echo
case $c in
a) pause=0;;
q) break;;
esac
fi
done
答え2
ループでカウンターを使用できますfor
。これはPOSIX互換シェルで動作します。
これは質問の最初のコードと同じです。
i=0
for f in /some/path/*.txt ; do
if [ "$((i += 1))" -gt 2 ] ; then
break
fi
echo "unpacking $f"
./prepare.sh "$f" && rm -v "$f"
done
書かれた通りザイルズの答えには、zsh
これをより簡単な方法で達成できる機能があります。 glob修飾子の説明については、この回答を参照してください。
for f in /some/path/*.txt(N[1,2]) ; do
echo "unpacking $f"
./prepare.sh "$f" && rm -v "$f"
done
ループを中断するのではなく、いくつかの入力を待つこともできます。
i=0
for f in /some/path/*.txt ; do
if [ "$((i += 1))" -gt 2 ] ; then
i=1
printf "press Enter to continue"
read dummy
fi
printf "unpacking %s\n" "$f"
./prepare.sh "$f" && rm -v "$f"
done
注1:条件付き分岐では、スクリプトは既にこの反復で次のグループの最初のファイルを処理しているため、代わりにi=1
使用します。i=0
ノート2:ループの終わりではなく始まりに数と条件を入れました。なぜなら、この時点で処理する他のファイルがあることが明らかであるからです。これにより、最後のファイル以降に一時停止するのを防ぎます。
ノート3:スクリプトを編集して引用符を追加しました$((i += 1))
。スティーブン・チャジェラス一つで言及されているコメントほとんどのPOSIX準拠シェルでは、算術拡張の結果に対してワイルドカードと分割が行われます。これは、特にIFS
10進数が含まれると(異常な場合だと思います)、望ましくない結果につながる可能性があります。また、パターンがどのファイルとも一致しない場合は、ループ反復が実行され、f
リテラルファイル名パターンに設定されます。これにより、エラーメッセージや予期しない結果が生じる可能性があります。これを防ぐには、if [ -f "$f" ] ...
次のようにループ本体を条件付きで作成します。これらの特別なケースが意図された(対話型)使用に関連しない場合は、スクリプトをより簡単に保つことができます。
答え3
Bashでは、ファイル名を配列に入れて配列を繰り返すことができます。そしてループは次のように繰り返されます。
#!/bin/bash
shopt -s nullglob
files=(/some/path/*.txt)
for (( i = 0; i < "${#files[@]}"; i += 2)); do
for f in "${files[@]:i:2}"; do
printf "processing %s\n" "$f";
done
read -p "press enter to continue with the next set (or end)..."
done
または、最後に可能なプロンプトを避けるために再構成しますが、単純さを失う対価を払うことになります。
#!/bin/bash
shopt -s nullglob
files=(/some/path/*.txt)
i=0
while true; do
for f in "${files[@]:i:2}"; do
printf "processing %s\n" "$f";
done
(( i += 2 ))
(( i >= "${#files[@]}" )) && break
read -p "press enter to continue with the next set..."
done
答え4
使用することをお勧めしますgnu-parallel
- タスクをスクリプトに入れる
#!/bin/bash
echo unpacking ${1}
/full/path/to/prepare.sh ${1} && rm -v ${1}
- 実行方法
gnu-parallel
(実行ファイルの実際の名前は、ディストリビューションとプログラムのインストール方法によって異なる場合があります)
parallel --halt soon,success=5 /path/to/script {} ::: *.txt
parallel
デフォルトでは、CPUごとに1つのジョブが実行されます。つまり、--halt soon,success=5
「5つのジョブが成功すると処理を停止しますが、実行中のジョブは完了するようにします」を意味します。{}
ファイル名を置き換え、引数リストの区切り:::
文字です。
ユーザー側で「続行」を待っていませんが、元のファイルを削除したため、プロセスを再開できます。