非常に多数のファイルを正しい順序でまとめます


23

私は命名されている約15,000ファイル持ってfile_1.pdbfile_2.pdb実行してためにこれらの数千程度などI缶猫:

cat file_{1..2000}.pdb >> file_all.pdb

ただし、15,000個のファイルに対してこれを行うと、エラーが発生します

-bash: /bin/cat: Argument list too long

私はこの問題を解決することを見ましたfind . -name xx -exec xxが、これはファイルが結合される順序を保持しません。どうすればこれを達成できますか?


3
10番目のファイルの名前は何ですか?(または、1桁以上の番号付けされた順序を持つファイル。)
roaima

私は(現在)ディレクトリにこれらのファイルを15,000個持っており、あなたのcat file_{1..15000}.pdbコンストラクトはうまく機能します。
ロアイマ

11
システムによって制限が異なります。getconf ARG_MAX教えてください。
-ilkkachu

3
質問を「数千」または「非常に多数の」ファイルに変更することを検討してください。同様の問題を抱えている他の人にとって、質問を見つけやすくするかもしれません。
-msouth

回答:


49

findsortおよびを使用してxargs

find . -maxdepth 1 -type f -name 'file_*.pdb' -print0 |
sort -zV |
xargs -0 cat >all.pdb

このfindコマンドは、関連するすべてのファイルを検索し、そのパス名を出力sortして「バージョンソート」を行い、正しい順序にします(ファイル名の数字が固定幅までゼロで埋められている場合は不要です-V)。xargsソートされたパス名のこのリストを取得し、catそれらを可能な限り大きなバッチで実行します。

これは、ファイル名に改行やスペースなどの奇妙な文字が含まれている場合でも機能するはずです。-print0with を使用しfindて、sortソートするNULで終わる名前を指定し、を使用してsortこれらを処理し-zます。 xargsまた、その-0フラグを使用してヌルで終わる名前を読み取ります。

名前がpatternと一致しないファイルに結果を書き込むことに注意してくださいfile_*.pdb


上記のソリューションでは、一部のユーティリティにいくつかの非標準フラグを使用しています。これらは、これらのユーティリティのGNU実装、および少なくともOpenBSDとmacOS実装によってサポートされています。

使用される非標準フラグは

  • -maxdepth 1find最上位のディレクトリのみを入力し、サブディレクトリは入力しないようにします。POSIXly、使用find . ! -name . -prune ...
  • -print0findヌルで終了するパス名を出力する(これはPOSIXによって考慮されましたが拒否されました)。-exec printf '%s\0' {} +代わりに使用できます。
  • -zsortヌル文字で終了するレコードを作成します。POSIXに相当するものはありません。
  • -Vsort例えば200後をソートする3。POSIXに相当するものはありませんが、ファイル名のプレフィックスが固定されている場合は、ファイル名の特定の部分を数値で置き換えることができます。
  • -0xargsヌル終了レコードを読み取ります。POSIXに相当するものはありません。POSIXlyでは、によって認識される形式でファイル名を引用する必要がありますxargs

パス名が適切に動作し、ディレクトリ構造がフラット(サブディレクトリなし)の場合、-Vwith を除き、これらのフラグなしで処理できsortます。


1
これには非標準のヌル終端は必要ありません。これらのファイル名は非常に退屈で、POSIXツールは完全に処理できます。
ケビン

6
また、askerの仕様としてprintf ‘file_%d.pdb\0’ {1..15000} | xargs -0 cat、またはKevinの要点を使用して、これをより簡潔に記述することもできecho file_{1..15000}.pdb | xargs catます。これらfindのファイルのファイルシステムを検索する必要があるため、ソリューションのオーバーヘッドはかなり大きくなりますが、一部のファイルが存在しない場合に便利です。
小次郎

4
@Kevinあなたが言っていることは真実ですが、より一般的な状況に当てはまる答えを持つことは間違いなく良いでしょう。この質問がある次の1000人のうち、一部の人はファイル名にスペースなどが含まれている可能性があります。
-msouth

1
@chrylis Aのリダイレクトは、コマンドの引数の一部では決してありません、それはだxargsというよりも、catそれは(各リダイレクトされcat、呼び出しが使用するxargs標準出力)。もし私たちが言っていたら、あなたが示唆しているのであれば、の代わりにxargs -0 sh -c 'cat >all.pdb'を使用することは理にかなっているでしょう。>>>
クサラナナンダ

1
sort -n -k1.6うまくいくようです(元のfile_nnnファイル名、またはsort -n -k1.5アンダースコアのないファイル)。
スコット

14

with 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>小数点数に一致するYに対するXことグロブ演算子はないで。されていないxy、それは任意の10進数だ。と同等extendedglob[0-9]##又はkshglob+([0-9])(一つ以上の桁))。

では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

これseq -f | xarg cat > が最もエレガントで効果的なソリューションです。(私見では)。
ハスター

トリッキーなファイル名を確認してください...たぶん '"./-It'\''s a trickier filename - %.17g.pdb"'
ハスター

@Hastur、おっと!はい、おかげで、別の引用構文に変更しました。あなたのもうまくいくでしょう。
ステファンシャゼラス

11

forループが可能であり、非常に簡単です。

for i in file_{1..15000}.pdb; do cat $i >> file_all.pdb; done

欠点はcat、何回も地獄を呼び出すことです。しかし、もしものをどうやって行うかを正確に思い出せずfind、呼び出しオーバーヘッドがあなたの状況でそれほど悪くないなら、それは心に留めておく価値があります。


echo $i;ループの本文に「進行状況インジケーター」として追加することがよくあります
ロルフ

3
seq 1 15000 | awk '{print "file_"$0".dat"}' | xargs cat > file_all.pdb

1
ここでawkはseqの仕事をすることができ、seqはawkの仕事をすることができますseq -f file_%.10g.pdb 15000seqこれは標準コマンドではないことに注意してください。
ステファンシャゼラス

ステファンに感謝します- seq -f これを行うには素晴らしい方法だと思います 。それを覚えています。
ラリーC

2

前提

あなたは、のためにそのエラーに負担してはならないだけで、その特定の名前形式の15Kファイル[ 12 ]

その展開を別のディレクトリから実行していて、各ファイルにパスを追加する必要がある場合、コマンドのサイズは大きくなり、もちろん発生する可能性があります。

解決策は、そのディレクトリからコマンドを実行します。

(cd That/Directory ; cat file_{1..2000}.pdb >> file_all.pdb )

最良の解決策代わりに私が悪いと推測し、あなたがファイルがあるディレクトリからそれを実行する場合...
私見最良の解決策はステファンシャゼラスのものです:

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

...各ワードに8バイトを追加してもARG_MAX、カーネル3.13.0で報告された2097142(2.1M)または「実際に実行できるコマンドの最大長使用」によりますxargs --show-limits

システムの出力を見てみましょう

getconf ARG_MAX
xargs --show-limits

遅延ガイド付きソリューション

このような場合、通常は時間効率の良いソリューションが得られるため、ブロックを使用することを好みます。
ロジック(ある場合)は、私が1 ... 1000 1001..2000などを書くにはあまりにも面倒だからです。
ですから、私にスクリプトを依頼してください。
出力が正しいことを確認した後にのみ、スクリプトにリダイレクトします。

...しかし、怠azineは心の状態です
私はアレルギーがあるのでxargs(ここで実際に使用xargsするべきでした)、使用方法を確認したくないので、次の例(tl; dr)のように時間通りにホイールの再発明を完了します。

ファイル名は制御されているため(スペース、改行なしなど)、以下のスクリプトのようなものを簡単に使用できることに注意してください。

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 [ 3 ](coreutilsから)を完全に取り除き、bashの変数を直接操作するか、Pythonを使用するか、acプログラムをコンパイルして[ 4 ] ...


はの%g略であることに注意してください%.6g。たとえば、1,000,000を1e + 06として表します。
ステファンシャゼラス

本当に怠け者はxargs、zsh zargsksh93のようなE2BIGの制限を回避するタスクのために設計されたツールを使用しますcommand -x
ステファンシャゼル

seqbashビルトインではなく、GNU coreutilsからのコマンドです。seq -f %g 1000000 1000000coreutilsの最新バージョンでも1e + 06を出力します。
ステファンシャゼラス

@StéphaneChazelas 怠azineは心の状態です。奇妙なことですが、見ることができて(そしてシリアル化されたコマンドの出力を視覚的に確認して)から実行にリダイレクトすると、より居心地がいいと感じます。その構造はxarg... よりも考えることを私に与えますが、私はそれが個人的であり、おそらく私だけに関連していることを理解しています。
ハスター

@StéphaneChazelasGotcha、正しい...修正済み。ありがとう。私はOPから提供された15kファイルでのみテストしました。
ハスター

0

それを行う別の方法は

(cat file_{1..499}.pdb; cat file_{500..999}.pdb; cat file_{1000..1499}.pdb; cat file_{1500..2000}.pdb) >> file_all.pdb
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.