
などの名前のファイルが約15,000個ありますfile_1.pdb
。file_2.pdb
次のようにして、約数千のファイルを順番に並べ替えることができます。
cat file_{1..2000}.pdb >> file_all.pdb
ただし、15,000個のファイルでこれを実行するとエラーが発生します。
-bash: /bin/cat: Argument list too long
私はこれを実行してこの問題が解決するのを見ましたが、find . -name xx -exec xx
これはファイルの関連付け順序を維持しません。この目標をどのように達成できますか?
答え1
find
、sort
およびxargs
:を使用してください。
find . -maxdepth 1 -type f -name 'file_*.pdb' -print0 |
sort -zV |
xargs -0 cat >all.pdb
このfind
コマンドはすべての関連ファイルを見つけ、sort
「バージョンソート」を実行してパス名を印刷して正しい順序で取得します(ファイル名の数字がすでに固定幅で0で埋められている場合は必要ありません-V
)。xargs
このソートされたパス名のリストを取得し、cat
できるだけ大きなバッチで実行します。
これは、ファイル名に奇妙な文字(改行やスペースなど)が含まれている場合にも機能します。-print0
with を使用してfind
nulsort
で終わる名前をソートし、sort
これらの名前を処理するために使用します-z
。 xargs
また、対応するフラグを使用してnullで終わる名前を読みます-0
。
名前がパターンと一致しないファイルに結果を書き込んでいることに注意してくださいfile_*.pdb
。
上記の解決策は、いくつかのユーティリティに対して非標準フラグを使用します。これらのユーティリティのGNU実装と、少なくともOpenBSDとmacOSの実装はこれらの機能をサポートします。
使用される非標準フラグは次のとおりです。
-maxdepth 1
、find
サブディレクトリではなく最上位ディレクトリにのみ入ります。 POSIXly、使用find . ! -name . -prune ...
-print0
、find
出力がnullで終わるパス名になります(POSIXでは考慮されますが拒否されます)。代わりに使用できます-exec printf '%s\0' {} +
。-z
、nullで終わるレコードを取得しますsort
。 POSIXに該当するものはありません。-V
、sort
並べ替え(たとえば200
、3
.POSIXと同等ではありませんが、ファイル名に固定接頭辞がある場合は、ファイル名の特定の部分を数値順に置き換えることができます。-0
,xargs
null で終わるレコードを読み込みます。 POSIXに該当するものはありません。 POSIXlyではxargs
。
-V
パス名が正しく機能し、ディレクトリ構造が単純(サブディレクトリなし)の場合を除き、これらのフラグは使用できませんsort
。
答え2
(zsh
この{1..15000}
演算子のソース):
autoload zargs # best in ~/.zshrc
zargs file_{1..15000}.pdb -- cat > file_all.pdb
または、file_<digits>.pdb
番号順にすべてのファイルに対して次の操作を行います。
zargs file_<->.pdb(n) -- cat > file_all.pdb
(ここでは、<x-y>
10進数xからyまで一致するグローバル演算子です。norx
がない場合は任意の10y
進数です。sまたはextendedglob
s (1つ以上の数値)と同じです。)[0-9]##
kshglob
+([0-9])
Useはksh93
組み込みcat
コマンドを使用します(したがって、システムコールの制限の影響を受けませんexecve()
。実装する):
command /opt/ast/bin/cat file_{1..15000}.pdb > file_all.pdb
bash
// zsh
( 'をksh93
サポートして組み込まれています)を使用してください。zsh
{x..y}
printf
printf '%s\n' file_{1..15000}.pdb | xargs cat > file_all.pdb
GNUシステムまたは互換システムでは、次のものも使用できますseq
。
seq -f 'file_%.17g.pdb' 15000 | xargs cat > file_all.pdb
ベースのソリューションでは、xargs
スペース、一重引用符、二重引用符、またはバックスラッシュを含むファイル名に特別な注意を払う必要があります。
と同様に、-It's a trickier filename - 12.pdb
以下を使用します。
seq -f "\"./-It's a trickier filename - %.17g.pdb\"" 15000 |
xargs cat > file_all.pdb
答え3
forループが可能で、とても簡単です。
for i in file_{1..15000}.pdb; do cat $i >> file_all.pdb; done
cat
欠点は、何度も呼び出すことです。ただし、これらの操作の実行方法を正確に覚えておらず、find
呼び出しのオーバーヘッドがそれほど悪くない場合は、覚えておくことをお勧めします。
答え4
前提
こんなミスをしてはいけないただその特定の名前形式のファイル15,000個[1、2]。
別のディレクトリで拡張機能を実行し、各ファイルにパスを追加する必要がある場合は、コマンドのサイズが大きくなります。もちろん、これが起こる可能性があります。
解決策このディレクトリからコマンドを実行します。
(cd That/Directory ; cat file_{1..2000}.pdb >> file_all.pdb )
最高のソリューション代わりに、私が間違って推測してファイルがあるディレクトリで実行している場合...
IMHOの最良の解決策は次のとおりです。ステファン・チャジェラスの作品:
seq -f 'file_%.17g.pdb' 15000 | xargs cat > file_all.pdb
printfまたはseqと一緒に使用され、15kファイルでテストされ、ファイル数のみを事前キャッシュしてはるかに高速でした(現在のファイルと同じディレクトリにあるOPファイルを除く)。
いくつかの言葉を教えてください
より長いシェルコマンドラインを渡すことができるはずです。
コマンドラインの長さは、15003文字を含む213914文字です。性格
cat file_{1..15000}.pdb " > file_all.pdb" | wc
...ワードあたり8バイトを追加しても333,938バイト(0.3M)になります。これは、カーネル3.13.0で報告されている2097142(2.1M)ARG_MAX
またはわずかに小さい2088232よりはるかに少ないです。「実際に使用できるコマンドの最大長」渡すxargs --show-limits
システムで次の出力を表示します。
getconf ARG_MAX
xargs --show-limits
怠惰なブートソリューション
この場合、ブロックを使用するのが一般的に時間効率の良いソリューションになるため、これをお勧めします。
(存在する場合)ロジックは、1...1000 1001..2000などを書くにはあまりにも怠惰です。
だからスクリプトを要求しました。
出力の正確性を確認したら、それをスクリプトにリダイレクトします。
...しかし、怠惰は心の状態です。
私はxargs
(ここで必ず使用する必要がありますxargs
)アレルギーがあり、どのように使用するかを確認したくなかったので、以下の例(tl;dr)のように車輪を再作成する作業を正式に終えました。
ファイル名が制御されるため(空白、改行なし...)、次のスクリプトのようなものを簡単に使用できます。
長すぎます。
バージョン1:最初のファイル番号、最後のファイル番号、ブロックサイズ、および出力ファイルをオプションのパラメータとして渡します。
#!/bin/bash
StartN=${1:-1} # First file number
EndN=${2:-15000} # Last file number
BlockN=${3:-100} # files in a Block
OutFile=${4:-"all.pdb"} # Output file name
CurrentStart=$StartN
for i in $(seq $StartN $BlockN $EndN)
do
CurrentEnd=$i ;
cat $(seq -f file_%.17g.pdb $CurrentStart $CurrentEnd) >> $OutFile;
CurrentStart=$(( CurrentEnd + 1 ))
done
# Here you may need to do a last iteration for the part cut from seq
[[ $EndN -ge $CurrentStart ]] &&
cat $(seq -f file_%.17g.pdb $CurrentStart $EndN) >> $OutFile;
バージョン2
拡張のためにbashを呼び出します(私のテストでは約20%遅い)。
#!/bin/bash
StartN=${1:-1} # First file number
EndN=${2:-15000} # Last file number
BlockN=${3:-100} # files in a Block
OutFile=${4:-"all.pdb"} # Output file name
CurrentStart=$StartN
for i in $(seq $StartN $BlockN $EndN)
do
CurrentEnd=$i ;
echo cat file_{$CurrentStart..$CurrentEnd}.pdb | /bin/bash >> $OutFile;
CurrentStart=$(( CurrentEnd + 1 ))
done
# Here you may need to do a last iteration for the part cut from seq
[[ $EndN -ge $CurrentStart ]] &&
echo cat file_{$CurrentStart..$EndN}.pdb | /bin/bash >> $OutFile;
もちろん、引き続き完全に削除することもできますseq
。サム](coreutilsから)bashで直接変数を使用するか、Pythonを使用するか、cプログラムをコンパイルして[4] ...