file_1.pdb
、file_2.pdb
などの名前のファイルが約15,000個あります。次のようにすることで、これらのうち数千個を順番に並べることができます。
cat file_{1..2000}.pdb >> file_all.pdb
ただし、これを15,000ファイルに対して実行すると、エラーが発生します
-bash: /bin/cat: Argument list too long
find . -name xx -exec xx
を実行することでこの問題が解決されるのを見てきましたが、これではファイルが結合される順序が保持されません。どうすればこれを達成できますか?
find
、sort
、xargs
の使用:
_find . -maxdepth 1 -type f -name 'file_*.pdb' -print0 |
sort -zV |
xargs -0 cat >all.pdb
_
find
コマンドは、関連するすべてのファイルを検索してから、それらのパス名をsort
に出力し、「バージョンの並べ替え」を行って正しい順序でファイルを取得します(ファイル名の数値が固定幅にゼロで埋められていた場合は、不要でした。 _-V
_)。 xargs
は、このソートされたパス名のリストを受け取り、これらに対してcat
をできるだけ大きなバッチで実行します。
これは、ファイル名に改行やスペースなどの奇妙な文字が含まれている場合でも機能します。 find
で_-print0
_を使用してsort
のヌル終了名を並べ替え、sort
が_-z
_を使用してこれらを処理します。 xargs
も、_-0
_フラグを使用してヌル文字で終了する名前を読み取ります。
名前がパターン_file_*.pdb
_と一致しないファイルに結果を書き込んでいることに注意してください。
上記のソリューションは、いくつかのユーティリティにいくつかの非標準フラグを使用しています。これらは、これらのユーティリティのGNU実装によって、および少なくともOpenBSDとmacOS実装によってサポートされています。
使用される非標準フラグは次のとおりです。
-maxdepth 1
_、find
を作成するには、最上位のディレクトリのみを入力し、サブディレクトリは入力しません。 POSIXly、_find . ! -name . -Prune ...
_を使用-print0
_、find
にヌル終了パス名を出力させる(これはPOSIXでは考慮されていましたが拒否されました)。代わりに_-exec printf '%s\0' {} +
_を使用できます。-z
_、sort
にNUL終了レコードを取得させる。 POSIXと同等のものはありません。-V
_、sort
を並べ替えます。 _200
_の後の_3
_。同等のPOSIXはありませんが、ファイル名に固定のプレフィックスが付いている場合は、ファイル名の特定の部分を数値で並べ替えることができます。-0
_、xargs
にヌル終了レコードを読み取らせる。 POSIXと同等のものはありません。 POSIXlyでは、xargs
で認識される形式でファイル名を引用符で囲む必要があります。パス名が適切に動作し、ディレクトリ構造がフラット(サブディレクトリなし)の場合、_-V
_とsort
を除いて、これらのフラグなしで実行できます。
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>
は、xからyまでの10進数に一致するグロブ演算子です。x
もy
もない場合は、10進数です。extendedglob
の[0-9]##
またはkshglob
の+([0-9])
(1桁以上))。
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
Forループは可能で、非常に単純です。
for i in file_{1..15000}.pdb; do cat $i >> file_all.pdb; done
マイナス面は、cat
を何度も呼び出すことです。しかし、find
を使用して正確に行う方法を思い出せず、呼び出しのオーバーヘッドが状況でそれほど悪くない場合は、覚えておく価値があります。
seq 1 15000 | awk '{print "file_"$0".dat"}' | xargs cat > file_all.pdb
特定の名前形式のonly15kファイルについては、このエラーが発生することはありません [ 1 、 2 ]。
別のディレクトリからその拡張を実行していて、各ファイルへのパスを追加する必要がある場合、コマンドのサイズが大きくなり、もちろんそれが発生する可能性があります。
Solutionそのディレクトリからコマンドを実行します。
(cd That/Directory ; cat file_{1..2000}.pdb >> file_all.pdb )
最善の解決策代わりに私が悪いと思って、ファイルがあるディレクトリからそれを実行した場合...
私見の最良の解決策は StéphaneChazelasのもの :
seq -f 'file_%.17g.pdb' 15000 | xargs cat > file_all.pdb
printfまたはseqを使用。事前にキャッシュされた数のみの15kファイルでテストされたものはさらに高速です(現在、ファイルが存在する同じディレクトリからのOPを除きます)。
シェルコマンドラインに渡せる時間が長くなるはずです。
コマンドラインは213914文字で、15003の単語が含まれていますcat file_{1..15000}.pdb " > file_all.pdb" | wc
...各Wordに8バイトを追加しても、カーネル3.13.0でARG_MAX
によって報告された2097142(2.1M)からはるかに下の333 938バイト(0.3M)またはわずかに小さい2088232 "実際に使用できるコマンドの最大長"by xargs --show-limits
システムの出力を確認してください
getconf ARG_MAX
xargs --show-limits
このような場合、通常は時間効率の高いソリューションが出てくるので、ブロックを使用することを好みます。
ロジック(もしあれば)は、私が1 ... 1000 1001..2000などを書くのが面倒です。
それで、私がスクリプトを実行してくれるように頼みます。
出力が正しいことを確認した後でのみ、スクリプトにリダイレクトします。
...しかし、怠惰は心の状態です。
私はxargs
にアレルギーがあるため(ここでは実際にxargs
を使用する必要がありました)、その使用方法を確認したくありません。以下の例のように(tl; dr)。
ファイル名は制御されているため(スペース、改行なし...)、以下のスクリプトのようなもので簡単に移動できます。
#!/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;
拡張のために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 ]...
それを行う別の方法は
(cat file_{1..499}.pdb; cat file_{500..999}.pdb; cat file_{1000..1499}.pdb; cat file_{1500..2000}.pdb) >> file_all.pdb