ファイルの最初の数行と最後の数行を表示するコマンド


23

多くの行を持つファイルがあり、各行には開始時にタイムスタンプがあります。

[Thread-3] (21/09/12 06:17:38:672) logged message from code.....

そのため、このログファイルから2つのことを頻繁にチェックします。

  1. グローバル条件と開始時刻を含む最初の数行も示されています。
  2. 最後の数行は、終了ステータスに他の情報が含まれています。

ファイルの最初と最後の数行だけを表示できる、便利な単一のコマンドはありますか?


2
グローバルな条件とは何head and tailですか?
デイジー

これがログファイルの一部です。私は精巧になろうとしていました。あなたはそれを無視することができます。
mtk

あなたの解決策は私には問題ありません。さらに便利にしたい場合は、シェル関数にします(エイリアスでも可能です)。
フォンブランド

@vonbrand問題は、私が知らないことですN
ベルンハルト

@Bernhard、私はsed(1)専門家ではありませんが、後で使用するために物を隠しておく方法があります。そこを見ると報われるかもしれません。OTOH、Perl(または何でも)を頻繁に使用する場合は、スクリプトを作成します。
フォンブランド

回答:


12

sedまたはawkを使用して、1つのコマンドで作成できます。しかし、あなたはスピード、原因で失うだろうsedawk、とにかく全体のファイルを介して実行する必要があります。速度の観点からは、関数を作成するか、毎回tail+ を組み合わせることをお勧めしheadます。これには、入力がパイプの場合は機能しないという欠点がありますが、シェルがサポートしている場合は、proccess置換を使用できます(以下の例を参照)。

first_last () {
    head -n 10 -- "$1"
    tail -n 10 -- "$1"
}

として起動します

first_last "/path/to/file_to_process"

プロセスの置換を続行するには(bash、zsh、kshのようなシェルのみ):

first_last <( command )

追伸 を追加しgrepて、「グローバル条件」が存在するかどうかを確認することもできます。


-n 10デフォルトですか?
l0b0

@ l0b0はい、デフォルトです。-n 10ここでは必要ありません。
ラッシュ

20

@rushは、head + tailを使用して大規模ファイルを効率化するのに適していますが、小ファイル(20行未満)では、一部の行が2回出力される場合があります。

{ head; tail;} < /path/to/file

同様に効率的ですが、上記の問題はありません。


ラッシュソリューションとは対照的に、これはPOSIXシェルでは機能しません。
マルコ

2
@マルコは?ここではPOSIXコンストラクトのみが使用されます。何がおかしいのですか?
ジル 'SO-悪であるのをやめる'

2
@Gilles私はスペースを見逃しました。zshでは{head; tail;} < file動作しますが、shでは失敗します。{ head; tail;} < file常に動作します。騒音でごめんなさい。
マルコ

@Marco、これに問題がある場合head、シェルではなくにあります。POSIXではhead、通常のファイルの場合、これらの10行を過ぎたファイルにカーソルを置く必要があります。非POSIX head実装(GNUヘッドの非常に古いバージョンは以前は非準拠でしたが、何十年も話しています)またはファイルがシーク可能でない場合(名前付きパイプまたはソケットなどですが、他のソリューションにも同じ問題があります)。
ステファンシャゼル

1
@FCTW、sudo sh -c '{ head; tail;} < /path/to/file'
ステファンシャゼル

9

{ head; tail; }解決策は、パイプ(またはソケットまたはその他のシーク不可能なファイル)で動作しませんのでhead、それは読み通りのブロックであまりにも多くのデータを消費することができ、潜在的に何を超えたファイルの中にカーソルを残してパイプに戻って求めることができないtailものとします選ぶ。

そのため、シェルのように一度に1文字を読み取るツールを使用できますread(ここでは、先頭行と末尾行の数を引数として使用する関数を使用しています)。

head_tail() {
  n=0
  while [ "$n" -lt "$1" ]; do
    IFS= read -r line || { printf %s "$line"; break; }
    printf '%s\n' "$line"
    n=$(($n + 1))
  done
  tail -n "${2-$1}"
}
seq 100 | head_tail 5 10
seq 20 | head_tail 5

またはtail、たとえば次のようにawkに実装します。

head_tail() {
  awk -v h="$1" -v t="${2-$1}" '
    {l[NR%t]=$0}
    NR<=h
    END{
      n=NR-t+1
      if(n <= h) n = h+1
      for (;n<=NR;n++) print l[n%t]
    }'
}

sed

head_tail() {
  sed -e "1,${1}b" -e :1 -e "$(($1+${2-$1})),\$!{N;b1" -e '}' -e 'N;D'
}

(ただし、一部のsed実装ではパターンスペースのサイズに低い制限があるため、テールラインの値が大きい場合は失敗します)。


4

bashプロセス置換を使用すると、次のことができます。

make_some_output | tee >(tail -n 2) >(head -n 2; cat >/dev/null) >/dev/null

行の順序が保証されているわけではないことに注意してください。ただし、約8kBを超えるファイルの場合、おそらくそうなります。この8kBのカットオフは読み取りバッファの一般的なサイズであり、| {head; tail;}小さなファイルでは機能しない理由に関連しています。

これcat >/dev/nullは、headパイプラインを存続させるために必要です。そうしないteeと、早期に終了しtail、から出力を取得しますが、終了ではなく入力の途中のどこかから出力されます。

最後に、なぜ別の場所>/dev/nullに移動tailするのではなく、なぜ|ですか?次の場合:

make_some_output | tee >(head -n 2; cat >/dev/null) | tail -n 2  # doesn't work

headの標準出力はtailコンソールではなくパイプに送られますが、これは私たちが望んでいるものではありません。


ヘッドまたはテールが必要な出力の書き込みを完了すると、stdinを閉じて終了します。それがSIGPIPEの由来です。通常、これは良いことです。彼らは出力の残りを破棄するので、パイプの反対側がそれを生成するのに時間を費やし続ける理由はありません。
デロバート

注文が支持される可能性が高いのはなぜですか?おそらくtailもっと長い時間をかけなければならないので、おそらく大きなファイル用になりますが、短い入力では約半分の時間で失敗することを期待しています(実際に見ています)。
ジル 'SO-悪であるのをやめる'

tee >(head) >(tail)同じ理由でSIGPIPEを取得します(>(...)ちなみに、これはzshとbashの両方でサポートされるようになりましたksh機能です)パイプも使用します。できます... | (trap '' PIPE; tee >(head) >(tail) > /dev/null)が、まだいくつかの壊れたパイプエラーメッセージが表示されteeます。
ステファンシャゼル

私のシステム(bash 4.2.37、coreutils 8.13)では、tailSIGPIPEによって殺されているのはteeでありtail、パイプに書き込まれていません。だから、それはからでなければなりませんkill()か?これは、|構文を使用している場合にのみ発生します。 straceそれteeは呼び出していないと言うkill()...だから多分bash
ジャンダー

1
@Janderは、同様に超える8Kを供給してみてくださいseq 100000 | tee >(head -n1) >(tail -n1) > /dev/null
ステファンChazelas

3

使用ed(ただし、ファイル全体をRAMに読み込みます):

# cf. http://wiki.bash-hackers.org/howto/edit-ed
printf '%s\n' 'H' '1,10p' '$-10,$p' 'q' | ed -s file

短い:ed -s file <<< $'11,$-10d\n,p\nq\n'
don_crissti 14

2

引数を使用できるようにする関数内のStephaneの最初のソリューション(BourneライクまたはPOSIXシェルで動作):

head_tail() {
    head "$@";
    tail "$@";
}

これで次のことができます。

head_tail -n 5 < /path/to/file

もちろん、これは1つのファイルのみを表示し、Stephaneのソリューションが(信頼できる)通常の(シーク可能な)ファイルでのみ動作することを前提としています。


2

-u--unbufferedGNUの)オプションsedは、使用することができますsed -u 2qにバッファリングされていないの代替としてhead -n2

$ seq 100|(sed -u 2q;tail -n2)
1
2
99
100

(head -n2;tail -n2)最後の行が、head次によって消費される入力のブロックの一部である場合、失敗します。

$ seq 1000|(head -n2;tail -n2)
1
2
999
1000
$ seq 100|(head -n2;tail -n2)
1
2

これが一番の答えです!魅力のように機能します!
ベン・ウスマン

1

今日私はこのようなことに遭遇しました。そこでは、ストリームの前から最後の行と数行だけが必要で、次のように思いつきました。

sed -n -e '1{h}' -e '2,3{H}' -e '${H;x;p}'

私はこれを次のように読みます:最初の行の内容でホールドスペースを初期化し、ホールドスペースに2-3行目を追加し、EOFでホールドスペースに最後の行を追加し、ホールドアンドパターンスペースを交換し、パターンを印刷しますスペース。

おそらくsed、私が持っているより多くの-fuを持っている人は、この質問に示されているストリームの最後の数行を印刷するためにこれを一般化する方法を見つけることができますが、私はそれを必要としませんでしたし、$アドレスに基づいて数学を行う簡単な方法を見つけることができませんでした中sedまたはおそらく場合にのみ、最後の数行がそれであるようにホールドスペースを管理することによりEOF達成されます。


1

Perlをインストールしている場合は試してください:

perl -e '@_ = <>; @_=@_[0, -3..-1]; print @_'

これはほとんどのファイルで機能しますが、処理する前にファイル全体をメモリに読み込みます。Perlスライスに慣れていない場合、角括弧内の「0」は「最初の行を取得」を意味し、「-3 ...- 1」は「最後の3行を取得」を意味します。両方をニーズに合わせて調整できます。本当に大きなファイルを処理する必要がある場合(「大きい」ファイルはRAMとおそらくスワップサイズに依存する可能性があります)、次の方法をお勧めします。

perl -e 'while($_=<>){@_=(@_,$_)[0,-3..-1]}; print @_'

反復ごとにスライスを作成するため、多少遅くなりますが、ファイルサイズには依存しません。

両方のコマンドは、パイプと通常のファイルの両方で機能するはずです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.