多くの行を含むファイルがあり、各行の最初にタイムスタンプがあります。
[Thread-3] (21/09/12 06:17:38:672) logged message from code.....
したがって、私はこのログファイルから2つのことを頻繁にチェックします。
ファイルの最初と最後の数行だけを表示できる簡単なコマンドはありますか?
sed
またはawk
を使用すると、1つのコマンドで作成できます。ただし、速度が落ちるので、sed
およびawk
はとにかくファイル全体を実行する必要があります。速度の観点からは、関数を作成するか、毎回tail
+ head
を組み合わせることをお勧めします。これには、入力がパイプの場合は機能しないという欠点がありますが、シェルがサポートしている場合は、プロセス置換を使用できます(以下の例を参照)。
first_last () {
head -n 10 -- "$1"
tail -n 10 -- "$1"
}
そしてちょうどそれを起動します
first_last "/path/to/file_to_process"
プロセスの置換を続行するには(シェルのようなbash、zsh、kshのみ):
first_last <( command )
ps。 grep
を追加して、「グローバル条件」が存在するかどうかを確認することもできます。
@Rushは、head + tailを使用するのが適切ですが、大きなファイルの方が効率的ですが、小さなファイル(<20行)の場合、一部の行が2回出力されることがあります。
{ head; tail;} < /path/to/file
同様に効率的ですが、上記の問題はありません。
{ head; tail; }
ソリューションはパイプ(またはソケットまたはその他のシーク不可能なファイル)では機能しません。これは、head
がブロックによって読み取り、パイプからカーソルを離れる可能性があるため、データを大量に消費する可能性があるためです。 tail
が選択するものを超えてファイル内。
したがって、シェルのread
のように一度に1文字ずつ読み取るツールを使用できます(ここでは、ヘッドラインとテールラインの数を引数として取る関数を使用しています)。
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
またはawkにtail
を次のように実装します:
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
実装では、パターンスペースのサイズに低い制限があるため、テールラインの数が大きい場合は失敗します)。
bash
プロセス置換を使用すると、次のことができます。
make_some_output | tee >(tail -n 2) >(head -n 2; cat >/dev/null) >/dev/null
行が正しいとは限らないことに注意してください。ただし、約8kBを超えるファイルの場合は、そうなる可能性が非常に高くなります。この8kBのカットオフは読み取りバッファーの一般的なサイズであり、| {head; tail;}
が小さなファイルに対して機能しない理由に関連しています。
head
パイプラインを存続させるには、cat >/dev/null
が必要です。そうしないと、tee
が早く終了し、tail
から出力が得られますが、入力の最後ではなく、途中のどこかから出力されます。
最後に、tail
を別の>/dev/null
に移動する代わりに、なぜ|
を使用するのですか?次の場合:
make_some_output | tee >(head -n 2; cat >/dev/null) | tail -n 2 # doesn't work
head
のstdoutは、コンソールではなくtail
へのパイプに送られますが、これはまったく必要ありません。
ed
を使用すると(ファイル全体がRAMに読み込まれます)):
# cf. http://wiki.bash-hackers.org/howto/edit-ed
printf '%s\n' 'H' '1,10p' '$-10,$p' 'q' | ed -s file
引数を使用できるようにするための関数でのステファンの最初のソリューション(任意のBourne-likeまたはPOSIXシェルで動作します):
head_tail() {
head "$@";
tail "$@";
}
これでこれを行うことができます:
head_tail -n 5 < /path/to/file
もちろん、これは1つのファイルだけを見ていて、Stephaneのソリューションが(確実に)通常の(シーク可能な)ファイルでのみ機能することを前提としています。
GNU sed
の-u
(--unbuffered
)オプションを使用すると、sed -u 2q
をhead -n2
のバッファなしの代替として使用できます。
$ seq 100|(sed -u 2q;tail -n2)
1
2
99
100
最後の行がhead
によって消費される入力のブロックの一部である場合、(head -n2;tail -n2)
は失敗します。
$ seq 1000|(head -n2;tail -n2)
1
2
999
1000
$ seq 100|(head -n2;tail -n2)
1
2
Perlがインストールされている場合は、Perlを試すことができます。
Perl -e '@_ = <>; @_=@_[0, -3..-1]; print @_'
これはほとんどのファイルで機能しますが、処理する前にファイル全体をメモリに読み込みます。 Perlスライスに慣れていない場合、角かっこ内の「0」は「最初の行を取る」を意味し、「-3 ...- 1」は「最後の3行を取る」ことを意味します。どちらもニーズに合わせて調整できます。本当に大きなファイルを処理する必要がある場合(「大きな」とは、RAMとスワップサイズに依存する場合があります)、次の方法を使用することをお勧めします。
Perl -e 'while($_=<>){@_=(@_,$_)[0,-3..-1]}; print @_'
毎回スライスになるため、多少遅くなる可能性がありますが、ファイルサイズには依存しません。
どちらのコマンドも、パイプ内と通常のファイルの両方で機能します。
今日は、ストリームの前から最後の行と数行だけが必要なこのような状況に遭遇し、次のことを思いつきました。
sed -n -e '1{h}' -e '2,3{H}' -e '${H;x;p}'
私はこれを次のように読みます:最初の行の内容でホールドスペースを初期化し、ホールドスペースにライン2-3を追加します= EOFホールドスペースに最後の行を追加し、ホールドをスワップします- and-pattern space、およびpattern spaceを印刷します。
おそらく、私が持っているよりもsed
- fuが多い人は、これを一般化して、この質問に示されているストリームの最後のfew行を出力する方法を理解できますが、私はそれを必要としませんでした。 sed
の$
アドレスに基づいて、またはおそらくEOF
のときに最後の数行のみが含まれるようにホールドスペースを管理することによって、数学を行う簡単な方法を見つけることができませんでした達した。