簡単なgrepの例を考えてみましょう。
$ psa aux | grep someApp
1000 11634 51.2 0.1 32824 9112 pts/1 SN+ 13:24 7:49 someApp
これは多くの情報を提供しますが、psコマンドの最初の行が欠落しているため、その情報のコンテキストはありません。 psの最初の行も表示したいです。
$ psa aux | someMagic someApp
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
1000 11634 51.2 0.1 32824 9112 pts/1 SN+ 13:24 7:49 someApp
もちろん、特にpsのgrepに正規表現を追加できます。
$ ps aux | grep -E "COMMAND|someApp"
しかし、最初の行も欲しい他の状況があるので、より一般的な解決策を好みます。
良い活用事例になりそうです"stdmeta"ファイル記述子。
答え1
良い考え
通常、grepではこれを行うことはできませんが、他のツールを使用できます。 AWKはすでに言及されていますが、sed
次のように使用することもできます。
sed -e '1p' -e '/youpattern/!d'
仕組み:
Sed ユーティリティは各ラインで個別に動作し、各ラインで指定されたコマンドを実行します。複数のオプションを指定して複数のコマンドを使用できます
-e
。各コマンドの前に範囲パラメーターを追加して、コマンドが特定の行に適用されるかどうかを指定できます。「1p」が最初のコマンドです。
p
一般的なコマンドを使用してすべての行を印刷します。ただし、適用する範囲を指定する前に数字を追加します。ここでは、1
最初の行を意味するwhichを使用します。より多くの行を印刷するには、印刷する最初の行と印刷する最後の行x,yp
を使用できます。たとえば、最初の3行を印刷するために使用できます。x
y
1,3p
次のコマンドは
d
通常、バッファ内のすべての行を削除します。このコマンドの前にはyourpattern
2/
文字の間に配置します。これは、コマンドを実行する必要がある行を指定する別の方法ですp
(最初にコマンドで行ったように、どの行を指定するか)。これは、コマンドが一致する行でのみ機能することを意味しますyourpattern
。これに加えて、コマンドの前に!
文字を使用してd
ロジックを反転します。これですべての行が削除されます。欲しくない指定されたパターンと一致します。最後に、sedはバッファに残っているすべての行を印刷します。ただし、一致しない行がバッファから削除されるため、一致する行だけが印刷されます。
要約すると、最初の行を印刷してから、パターンと一致しない入力からすべての行を削除します。残りの行が印刷されます。したがって、対応する行だけが印刷されます。する一致するパターン)。
最初の行の質問
コメントで述べたように、このアプローチには問題があります。指定されたパターンが最初の行に一致する場合は2回印刷されます(p
コマンドで1回、一致のために1回)。次の 2 つの方法でこれを回避できます。
後ろに
1d
コマンドを追加します1p
。すでに述べたように、d
このコマンドはバッファから行を削除し、範囲を数値1として指定します。これは最初の行だけを削除することを意味します。だからコマンドはsed -e '1p' -e '1d' -e '/youpattern/!d'
1b
代わりにコマンドを使用してください1p
。これはトリックです。b
コマンドを使用すると、ラベル付きの他のコマンドに移動できます(したがって、一部のコマンドは省略可能です)。ただし、このタグを指定しないと(例と同様に)、残りの行は無視してコマンドの最後に移動します。したがって、私たちの場合、最後のd
コマンドはバッファからこの行を削除しません。
完全な例:
ps aux | sed -e '1b' -e '/syslog/!d'
セミコロンを使用
一部の実装では、sed
複数のオプションを使用する代わりに、セミコロンを使用してコマンドを区切ることで-e
入力時間を節約できます。したがって、移植性に興味がない場合、コマンドは少なくとも実装ではps aux | sed '1b;/syslog/!d'
機能GNU sed
しますbusybox
。
クレイジーな方法
しかし、これはgrepを使用してこれを行う非常にクレイジーな方法です。これは確かに最適ではありません。学習目的でのみ公開しているがシステムに他のツールがない場合は、次のものを使用できます。
ps aux | grep -n '.*' | grep -e '\(^1:\)\|syslog'
どのように動作しますか?
まず
-n
、オプションを使用して各行の前に行番号を追加します。私たちは一致するすべての行を計算したいと思います.*
。空行も含まれます。コメントで提案したように「^」も一致させることができ、結果は同じです。\|
次に、ORとして機能する特殊文字を使用できるように拡張正規表現を使用します。したがって、行が1:
(最初の行)で始まる場合、またはパターンが含まれている場合(この場合syslog
)は一致します。
行番号の問題
今問題は、出力にこの見苦しい行番号が表示されることです。これが問題の場合は、以下を使用して削除できますcut
。
ps aux | grep -n '.*' | grep -e '\(^1:\)\|syslog' | cut -d ':' -f2-
-d
オプションは、区切り文字を指定して-f
印刷するフィールド(または列)を指定します。したがって、すべての文字のすべての行を切り取り、:
2番目の列とすべての後続の列のみを印刷しようとします。これにより、最初の列と対応する区切り文字が効果的に削除されます。これは私たちに必要なものです。
答え2
awk
代替使用についてどう思いますかgrep
?
chopper:~> ps aux | awk 'NR == 1 || /syslogd/'
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
root 19 0.0 0.0 2518684 1160 ?? Ss 26Aug12 1:00.22 /usr/sbin/syslogd
mrb 574 0.0 0.0 2432852 696 s006 R+ 8:04am 0:00.00 awk NR == 1 || /syslogd/
NR == 1
:レコード数== 1;最初の行||
:または:/syslogd/
:検索するパターン
pgrep
これは、ユーザーが表示する出力よりもスクリプトに適していますが、見る価値があるかもしれません。ただし、grep
コマンド自体が出力に表示されるのを防ぎます。
chopper:~> pgrep -l syslogd
19 syslogd
答え3
ps aux | { IFS= read -r line; printf '%s\n' "$line";grep someApp;}
head
組み込みhead
(ksh93
アクティブ化を使用) などの一部の実装の場合、builtin head
すべてのビルドksh93
に含まれるわけではありません。
ps aux | { head -n1;grep someApp;}
ただし、ほとんどのhead
実装では、入力を取得できない場合(パイプなど)、チャンクで入力を読み取ると機能しません。
そして:
{ head -1;grep ok;} <<END
this is a test
this line should be ok
not this one
END
ほとんどの実装では、次のようにhead
しか取得できません。
this is a test
this line should be ok
ここでは、パイプの代わりに一時ファイルを使用してドキュメントを実装するシェルを使用してください。
このコマンドは、1行以上の入力を読み取らないようにするため、line
利用可能な場所で動作します(以前は標準コマンドでしたが、機能へのアクセスにより標準から削除されました)。IFS= read -r
(choの場合はditの場合と混同しないでください)をzsh
使用することもできます。また、NULバイトをブロックしない唯一のシェルでもあります。IFS= read -re
-e
e
bash
-e
e
read
答え4
私はヘッダーを送信する傾向があります標準エラー:
ps | (IFS= read -r HEADER; echo "$HEADER" >&2; cat) | grep ps
一般に、人間が読む目的には、この程度で十分です。たとえば、
PID TTY TIME CMD
4738 pts/0 00:00:00 ps
角かっこ内のセクションは、一般的な使用のために独自のスクリプトに入れることができます。
sort
さらに、出力をさらにパイプでき、ヘッダーが一番上に保持されるという追加の利便性もあります。