前提

前提

などの名前のファイルが約15,000個ありますfile_1.pdbfile_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

findsortおよびxargs:を使用してください。

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

このfindコマンドはすべての関連ファイルを見つけ、sort「バージョンソート」を実行してパス名を印刷して正しい順序で取得します(ファイル名の数字がすでに固定幅で0で埋められている場合は必要ありません-V)。xargsこのソートされたパス名のリストを取得し、catできるだけ大きなバッチで実行します。

これは、ファイル名に奇妙な文字(改行やスペースなど)が含まれている場合にも機能します。-print0with を使用してfindnulsortで終わる名前をソートし、sortこれらの名前を処理するために使用します-zxargsまた、対応するフラグを使用してnullで終わる名前を読みます-0

名前がパターンと一致しないファイルに結果を書き込んでいることに注意してくださいfile_*.pdb


上記の解決策は、いくつかのユーティリティに対して非標準フラグを使用します。これらのユーティリティのGNU実装と、少なくともOpenBSDとmacOSの実装はこれらの機能をサポートします。

使用される非標準フラグは次のとおりです。

  • -maxdepth 1findサブディレクトリではなく最上位ディレクトリにのみ入ります。 POSIXly、使用find . ! -name . -prune ...
  • -print0find出力がnullで終わるパス名になります(POSIXでは考慮されますが拒否されます)。代わりに使用できます-exec printf '%s\0' {} +
  • -z、nullで終わるレコードを取得しますsort。 POSIXに該当するものはありません。
  • -Vsort並べ替え(たとえば2003.POSIXと同等ではありませんが、ファイル名に固定接頭辞がある場合は、ファイル名の特定の部分を数値順に置き換えることができます。
  • -0, xargsnull で終わるレコードを読み込みます。 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またはextendedglobs (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個[12]

別のディレクトリで拡張機能を実行し、各ファイルにパスを追加する必要がある場合は、コマンドのサイズが大きくなります。もちろん、これが起こる可能性があります。

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

(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] ...

関連情報