測定された時間の一部は次のとおりです。

測定された時間の一部は次のとおりです。

cat大きなテキストファイル(> 2GB)があり、行だけを書きたいとしますXY例:57890000〜57890010)。

私が理解している限り、パイプを接続するか、その逆にすることができheadますtail

head -A /path/to/file | tail -B

または代替的に

tail -C /path/to/file | head -D

そのうちA、、、Bはファイルの行数に基づいて計算できますCDXY

しかし、このアプローチには2つの問題があります。

  1. ABを計算する必要がありますCD
  2. コマンドはpipe互いに渡すことができます。もっとあります私が読みたい行よりも(例えば、大きなファイルの途中で数行だけ読む場合)

シェルで私が望む行だけを処理して出力する方法はありますか? (同時にANDのみ可能ですX。)Y

答え1

解決策を提案したいが、sed完全性のために

awk 'NR >= 57890000 && NR <= 57890010' /path/to/file

最後の行の後ろから切り取るには:

awk 'NR < 57890000 { next } { print } NR == 57890010 { exit }' /path/to/file

速度テスト(ここではmacOS、他のシステムではYMMV):

  • 100,000,000ラインファイルの生成seq 100000000 > test.in
  • 読み取った行数 50,000,000-50,000,010
  • 特別な順序なしでテスト
  • realbash組み込み時間レポートtime
 4.373  4.418  4.395    tail -n+50000000 test.in | head -n10
 5.210  5.179  6.181    sed -n '50000000,50000010p;57890010q' test.in
 5.525  5.475  5.488    head -n50000010 test.in | tail -n10
 8.497  8.352  8.438    sed -n '50000000,50000010p' test.in
22.826 23.154 23.195    tail -n50000001 test.in | head -n10
25.694 25.908 27.638    ed -s test.in <<<"50000000,50000010p"
31.348 28.140 30.574    awk 'NR<57890000{next}1;NR==57890010{exit}' test.in
51.359 50.919 51.127    awk 'NR >= 57890000 && NR <= 57890010' test.in

これは決して正確なベンチマークではありませんが、各コマンドの相対速度に関する良いアイデアを提供するのに十分な違いがあり、繰り返し可能です。

*:最初の2つの間の合計をsed -n p;q除いて、head|tail本質的に同じように見えます。

答え2

XからYまでの行(1から始まる番号)を含めるには、次のようにします。

tail -n "+$X" /path/to/file | head -n "$((Y-X+1))"

tail最初のX-1行は読み取りおよび削除され(この問題は解決されません)、次の行は読み取りおよび印刷されます。head要求された行数を読み取り、印刷してから終了します。出口からheadtail信号パイプラインバッファサイズ(通常は数キロバイト)よりも入力ファイルからより多くの行を読み取らないように信号を送って死にます。

またはゴリキプールsedを使用することをお勧めします。

sed -n -e "$X,$Y p" -e "$Y q" /path/to/file

ただし、sedソリューションはかなり遅いです(少なくともGNUユーティリティとBusyboxユーティリティの場合、パイプが遅く、sedが高速オペレーティングシステムからほとんどのファイルを抽出する場合、sedはより競争力があるかもしれません)。以下はLinuxでの高速ベンチマークです。データは から生成され、seq 100000000 >/tmp/a環境は Linux/amd64、/tmp環境は tmpfs、マシンはアイドル状態でスワップされません。

real  user  sys    command
 0.47  0.32  0.12  </tmp/a tail -n +50000001 | head -n 10 #GNU
 0.86  0.64  0.21  </tmp/a tail -n +50000001 | head -n 10 #BusyBox
 3.57  3.41  0.14  sed -n -e '50000000,50000010 p' -e '50000010q' /tmp/a #GNU
11.91 11.68  0.14  sed -n -e '50000000,50000010 p' -e '50000010q' /tmp/a #BusyBox
 1.04  0.60  0.46  </tmp/a tail -n +50000001 | head -n 40000001 >/dev/null #GNU
 7.12  6.58  0.55  </tmp/a tail -n +50000001 | head -n 40000001 >/dev/null #BusyBox
 9.95  9.54  0.28  sed -n -e '50000000,90000000 p' -e '90000000q' /tmp/a >/dev/null #GNU
23.76 23.13  0.31  sed -n -e '50000000,90000000 p' -e '90000000q' /tmp/a >/dev/null #BusyBox

使用したいバイト範囲を知っている場合は、最初に直接移動してより速く抽出できます。ただし、行の場合は最初から読み、改行回数を数える必要があります。チャンクサイズbを使用して0から始まり、x(含む)からy(除外)までチャンクを抽出するには、次の手順を実行します。

dd bs="$b" seek="$x" count="$((y-x))" </path/to/file

答え3

これhead | tailは、これを達成するための最良かつ最も「慣用的な」方法の1つです。

X=57890000
Y=57890010
< infile.txt head -n "$Y" | tail -n +"$X"

Gilesがコメントで指摘したように、より速い方法は

< infile.txt tail -n +"$X" | head -n "$((Y - X))"

早い理由は最初X-1方法とは異なり、ラインはパイプを通過する必要はありませんhead | tail

あなたの質問フレーズは少し誤解を招く可能性があり、このアプローチに対する根拠のない不安を説明することができます。

  • A、、、Bを計算する必要があると言われましたがCD見てわかるように、ファイルの行数は必要なく、最大1回の計算が必要であり、とにかくシェルがこれを行うことができます。

  • パイプがあまりにも多くの行を読むかどうか心配です。実際、これは本当ではありません。tail | headファイルI/O側から得られるほど効率的です。まず、必要な最小限の努力を検討してください。Xファイルの3行目、唯一の一般的な方法は、各バイトを読み取って計算するときに停止することです。Xファイルのオフセットを予測できないため、改行文字X'列。 *X*行に達すると、印刷する前にすべての行を読み取る必要があります。はい'第1行。したがって、本を少なく読むことを避ける方法はありません。はいワイヤー。head -n $Yもう読んではいけません。はい行(最も近いバッファ単位に丸められますが、バッファを正しく使用するとパフォーマンスが向上するため、オーバーヘッドを心配する必要はありません)。また、tailそれ以上は読み込まれていないので、できるだけ少ない数の行を読み取るようにマークしheadましたhead | tail(再度、無視できる無視できるバッファリングも含みます)。パイプなしの単一ツールアプローチの唯一の効率性の利点は、プロセス数が少ないことです(したがってオーバーヘッドも少なくなります)。

答え4

選択する範囲がわかっている場合は、最初の行からlStart最後の行まで次をlEnd計算できます。

lCount="$((lEnd-lStart+1))"

総行数がわかっている場合は、lAllファイルの終わりまでの距離も計算できます。

toEnd="$((lAll-lStart+1))"

それから私たちは次のことを知ります:

"how far from the start"            ($lStart) and
"how far from the end of the file"  ($toEnd).

最も小さいものを選択してくださいtailnumber

tailnumber="$toEnd"; (( toEnd > lStart )) && tailnumber="+$linestart"

常に最速の実行コマンドを使用できます。

tail -n"${tailnumber}" ${thefile} | head -n${lCount}

$linestart選択するときは、追加のプラス記号(「+」​​)を参照してください。

唯一の注意点は、合計行数が必要なため、検索に少し時間がかかることがあります。
いつものように:

linesall="$(wc -l < "$thefile" )"

測定された時間の一部は次のとおりです。

lStart |500| lEnd |500| lCount |11|
real   user   sys    frac
0.002  0.000  0.000  0.00  | command == tail -n"+500" test.in | head -n1
0.002  0.000  0.000  0.00  | command == tail -n+500 test.in | head -n1
3.230  2.520  0.700  99.68 | command == tail -n99999501 test.in | head -n1
0.001  0.000  0.000  0.00  | command == head -n500 test.in | tail -n1
0.001  0.000  0.000  0.00  | command == sed -n -e "500,500p;500q" test.in
0.002  0.000  0.000  0.00  | command == awk 'NR<'500'{next}1;NR=='500'{exit}' test.in


lStart |50000000| lEnd |50000010| lCount |11|
real   user   sys    frac
0.977  0.644  0.328  99.50 | command == tail -n"+50000000" test.in | head -n11
1.069  0.756  0.308  99.58 | command == tail -n+50000000 test.in | head -n11
1.823  1.512  0.308  99.85 | command == tail -n50000001 test.in | head -n11
1.950  2.396  1.284  188.77| command == head -n50000010 test.in | tail -n11
5.477  5.116  0.348  99.76 | command == sed -n -e "50000000,50000010p;50000010q" test.in
10.124  9.669  0.448  99.92| command == awk 'NR<'50000000'{next}1;NR=='50000010'{exit}' test.in


lStart |99999000| lEnd |99999010| lCount |11|
real   user   sys    frac
0.001  0.000  0.000  0.00  | command == tail -n"1001" test.in | head -n11
1.960  1.292  0.660  99.61 | command == tail -n+99999000 test.in | head -n11
0.001  0.000  0.000  0.00  | command == tail -n1001 test.in | head -n11
4.043  4.704  2.704  183.25| command == head -n99999010 test.in | tail -n11
10.346  9.641  0.692  99.88| command == sed -n -e "99999000,99999010p;99999010q" test.in
21.653  20.873  0.744  99.83 | command == awk 'NR<'99999000'{next}1;NR=='99999010'{exit}' test.in

選択したパスが始点または終点に近い場合、時間が大幅に変わる可能性があることに注意してください。ファイルの片側でうまく実行されるように見えるコマンドは、ファイルのもう一方の側では非常に遅くなる可能性があります。

関連情報