特定の行を_および_ファイルの最初の行をgrepする方法は?


76

次のような単純な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」ファイル記述子の適切な使用例のようです。


9
これらの回答に必要な複雑さは、使いやすさの基準で測定すると、「1つのことをうまくやる」というUnixの哲学が、私たちを失敗させることがあることを示しています。列ラベルが表示されたままになっている)は、アプローチの欠点を示しています:場合によっては、非常にきれいに合わないことがあります。以下のようなツールは、なぜこれがあるackので有用であり、なぜperl急上昇過去sedawk部品がコヒーレント全体にまとめることが重要です:人気が、など。
iconoclast

3
もちろん、この特定の例では、-C引数を使用することができ、psgrepにパイプする必要はありません。例ps u -C someAppまたはps u -C app1 -C app2 -C app3
cas

1
@iconoclast:もちろん、Unixyソリューションは、複数の行を多重化して、それぞれ異なるフィルターセットでフィルター処理できるツールになります。ps aux | { head -1; grep foo; }@Nahuel Fouilleul が言及した一般的なバージョンです(必要に応じてその場でリコールできる唯一のソリューションです)
リーライアン

@iconoclast:ツールの経験がなく、ツールの知識がなく、ツールが本当に上手くやっていることは常に完全に役に立たないように思われます。コマンドをよく知っていることは、使いやすさの重要な部分ではなく、細かいマニュアルと練習を読むことの重要な部分です。これらのツールは何十年も使用されてきました。彼らは非常にうまく(そしてきれいに)一緒に動作します。
ЯрославРахматуллин

@ЯрославРахматуллин:あなたが私が言ったことを完全に誤解したかもしれないと思う。(おそらく英語があなたの第一言語ではないのでしょうか?)「使いやすさ」は、ユーティリティ(または「有用性」)ではなくUX(「ユーザーエクスペリエンス」)に関連しています。単純な操作がこの複雑な場合、使いやすさを損なうことは、ツールが役に立たないということと同じではないことを指摘します。明らかに、それらは無駄ではありません。彼らの役に立たないと言う人は誰もいません。
iconoclast

回答:


67

良い方法

通常、grepでこれを行うことはできませんが、他のツールを使用できます。AWKはすでに言及されていますがsed、次のようにを使用することもできます。

sed -e '1p' -e '/youpattern/!d'

使い方:

  1. Sedユーティリティは各行で個別に動作し、各行で指定されたコマンドを実行します。複数の-eオプションを指定して、複数のコマンドを使用できます。このコマンドを特定の行に適用するかどうかを指定する範囲パラメーターを各コマンドの先頭に追加できます。

  2. 「1p」は最初のコマンドです。p通常はすべての行を出力するコマンドを使用します。ただし、適用する範囲を指定する数値を先頭に追加します。ここでは、1最初の行を意味します。より多くの行を印刷する場合x,ypx最初の行を印刷する場所、y最後の行を印刷する場所を使用できます。たとえば、最初の3行を印刷するには、次を使用します。1,3p

  3. 次のコマンドはd、通常、バッファーからすべての行を削除します。このコマンドの前にyourpattern、2つの/文字の間に置きます。これは、pコマンドを実行する行をアドレス指定するもう1つの方法です(最初はcommandで行ったようにどの行を指定することでした)。これは、コマンドが一致する行に対してのみ機能することを意味しyourpatternます。例外として、コマンドの!前にdその論理を反転させる文字を使用します。そのため、指定したパターンに一致しないすべての行が削除されます。

  4. 最後に、sedはバッファーに残っているすべての行を出力します。ただし、バッファーから一致しない行を削除したため、一致する行のみが印刷されます。

要約すると、1行目を印刷してから、パターンと一致しないすべての行を入力から削除します。行の残りの部分は(これだけライン印刷され実行パターンに一致します)。

最初の行の問題

コメントで述べたように、このアプローチには問題があります。指定されたパターンが最初の行にも一致する場合、2回印刷されます(pコマンドにより1回、一致により1回)。これは次の2つの方法で回避できます。

  1. 1d後にコマンドを追加し1pます。既に述べたように、dコマンドはバッファーから行を削除し、その範囲を番号1で指定します。つまり、最初の行のみが削除されます。したがって、コマンドはsed -e '1p' -e '1d' -e '/youpattern/!d'

  2. 1b代わりにコマンドを使用し1pます。それはトリックです。bcommandを使用すると、ラベルで指定された他のコマンドにジャンプできます(これにより、一部のコマンドを省略できます)。ただし、このラベルが指定されていない場合(この例のように)、コマンドの最後にジャンプし、残りのコマンドを無視します。したがって、この場合、最後の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'

使い方

  1. 最初に、-n各行の前に行番号を追加するオプションを使用します。一致するすべての行(.*空の行も含む)を列挙します。コメントで示唆されているように、「^」と一致させることもできます。結果は同じです。

  2. 次に、拡張正規表現を使用しているため\|、ORとして機能する特殊文字を使用できます。したがって、行が1:(最初の行)で始まるか、パターンを含む(この場合はsyslog)場合に一致します。

行番号の問題

問題は、出力でこのい行番号を取得していることです。これが問題である場合はcut、次のようにを使用して削除できます。

ps aux | grep -n '.*' | grep -e '\(^1:\)\|syslog' | cut -d ':' -f2-

-dオプションは区切り文字を-f指定し、印刷するフィールド(または列)を指定します。したがって、すべての:文字の各行を切り取り、2番目以降の列のみを印刷します。これにより、区切り文字で最初の列が効果的に削除されます。これはまさに必要なものです。


4
行番号付けcat -nも同様に行うことができ、これに対して乱用されたgrepのように、より明確に見えるでしょう。
アルフェ

1
nl空行をカウントしませんが(行番号なしで印刷します)、cat -n先行するスペースで番号付けをフォーマットし、grep -n .空行をまったく削除し、コロンを追加します。すべてに... er ...機能があります;
Alfe

2
非常に教育的なよく書かれた答え。「Pretend」(冒頭近く)を「Prepend」に置き換えようとしましたが、より多くの変更が必要であり、投稿のランダムながらくたを変更する気がなかったので、修正することをお勧めします。
ビルK

2
ps aux | sed '1p;/pattern/!d'patternに一致する場合、最初の行を2回印刷しますbコマンドを使用することをお勧めしますps aux | sed -e 1b -e '/pattern/!d'cat -nPOSIXではありません。grep -n '^'すべての行に番号を付けます(空行がないps出力の問題ではありません)。nl -ba -d $'\n'すべての行に番号を付けます。
ステファンシャゼラス

2
1b;...ポータブルでもPOSIXでもないことに注意してください。"b"の後に他のコマンドはないので、改行または別の-e式が必要です。
ステファンシャゼラス

58

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

とても素敵です、ありがとう。また、これは将来の拡張のためにうまくスクリプト化できます。
dotancohen

私はawkを学ぶ必要があります。非常に素晴らしい。
user606723

30
ps aux | { read line;echo "$line";grep someApp;}

編集:コメントの後

ps aux | { head -1;grep someApp;}

head -1はすべての入力を読みますが、それをテストした後、それも動作します。

{ head -1;grep ok;} <<END
this is a test
this line should be ok
not this one
END

出力は

this is a test
this line should be ok

2
それが、bashで直接綴られたアイデアです。これについて複数の評価をしたいと思います。{ IFS='' read line; ... }ヘッダーがスペースで始まる場合に使用するだけです。
アルフェ

これがない正確に問題を直接攻撃します。いいね!
dotancohen

3
head -1読み取り/エコーコンボの代わりに使用するだけです。
chepner

1
まあ、それhead -n1は私のbash で動作します。これはおそらく実装固有のものです。この場合、私の頭は入力全体を読み取らず、最初の行のみを読み取り、残りを入力バッファーに残します。
レジストフアダムスキー

2
head -n1は短くなりますが、POSIX仕様でさえ、どの程度の入力を読み取ることができるかについては黙っているように見えるので、おそらくread line; echo $lineより移植性が高いでしょう。
chepner

14

PSは内部フィルターをサポートし、

bashプロセスを探しているとします:

ps -C bash -f

という名前のすべてのプロセスをリストしますbash


ありがとう、それは知ってうれしいです。ただし、特にPythonから開始されたスクリプトは見つかりません。
dotancohen

6

私はヘッダーをstderrに送信する傾向があります:

ps | (IFS= read -r HEADER; echo "$HEADER" >&2; cat) | grep ps

これは通常、人間の読書目的には十分です。例えば:

  PID TTY          TIME CMD
 4738 pts/0    00:00:00 ps

ブラケット部分は、一般的な使用のために独自のスクリプトに入ることができます。

出力をさらに(sortなどに)パイプすることができ、ヘッダーが一番上に残るという追加の利便性があります。


5

また、使用することができますteehead

ps aux | tee >(head -n1) | grep syslog

ただし、シグナルteeを無視できない限りSIGPIPE(たとえば、ここ説明を参照)、このアプローチには信頼性のある回避策が必要であることに注意してください。回避策は、SIGPIPEシグナルを無視することです。これは、たとえば、bashのようなシェルで次のように実行できます。

trap '' PIPE    # ignore SIGPIPE
ps aux | tee >(head -n1) 2> /dev/null | grep syslog
trap - PIPE     # restore SIGPIPE handling

また、出力順序は保証されないことに注意してください。


動作するためにこれに依存することはありません。初めて実行したとき(zsh)、grepの結果の下に列ヘッダーが生成されました。二度目は大丈夫だった。
Rqomey

1
私はまだこれを見ていませんが、信頼性を高める1つの方法は、パイプラインでgrep:の前に小さな遅延を挿入することです| { sleep .5; cat }
トール

2
並行性の問題を回避するためにスリープを追加することは、常にハックです。これはうまくいくかもしれませんが、暗黒面への一歩です。-1。
アルフェ

1
この回答をしようとしたときに、私は私が設定し、他のいくつかの奇妙な問題を持っていたチェックするために質問を
Rqomey

これはteeの興味深い使用方法ですが、信頼性が低く、多くの場合、出力行のみを出力し、ヘッダー行は出力しません。
dotancohen

4

おそらく2つのpsコマンドが最も簡単でしょう。

$ ps aux | head -1 && ps aux | grep someApp
USER             PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
100         3304   0.0  0.2  2466308   6476   ??  Ss    2Sep12   0:01.75 /usr/bin/someApp

2
主に最初と2番目のps aux呼び出しで状況が変わる可能性があるため、このソリューションが好きではありません...そして、その静的な最初の行だけが必要な場合は、手動でエコーしませんか?
シャドゥール

1
この状況では、2つの呼び出し間の変更を気にする必要はありません。最初は、常に2番目の出力に適合する見出しのみを提供します。
アルフェ

2
これがなぜ投票されたのかはわかりませんが、確かに実行可能なオプションです。賛成。
dotancohen

4

pidstatを以下で使用できます。

pidstat -C someApp
or
pidstat -p <PID>

例:

# pidstat -C java
Linux 3.0.26-0.7-default (hostname)    09/12/12        _x86_64_

13:41:21          PID    %usr %system  %guest    %CPU   CPU  Command
13:41:21         3671    0.07    0.02    0.00    0.09     1  java

詳細:http : //linux.die.net/man/1/pidstat


ありがとう、それは知ってうれしいです。ただし、特にPythonから開始されたスクリプトは見つかりません。
dotancohen

4

テストのために、最初に.bashrcファイルに以下を入れるか、シェルにコピー/貼り付けします。

function psls { 
ps aux|head -1 && ps aux|grep "$1"|grep -v grep;
}

使用法:psls [grepパターン]

$ psls someApp
USER             PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
root              21   0.0  0.0  2467312   1116   ??  Ss   Tue07PM   0:00.17 /sbin/someApp

.bashrc(または、代わりに.bash_profileをそこに置いた場合)を入手してください:

source ~/.bashrc

この関数は、シェルコマンドラインでも自動補完されます。別の回答で述べたように、最初の行をファイルにパイプして、psへの1つの呼び出しを保存できます。


1
いいですね、私は長年その種の機能を使用しています。私は呼んで私のバージョンpslのみを呼び出し、psおよびgrep一度それぞれ(及び必要はありませんがhead)。
アダム・カッツ

3

ソートしますが、ヘッダー行を上部に保持します

# print the header (the first line of input)
# and then run the specified command on the body (the rest of the input)
# use it in a pipeline, e.g. ps | body grep somepattern
body() {
    IFS= read -r header
    printf '%s\n' "$header"
    "$@"
}

そして、このように使用します

$ ps aux | body grep 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

おかげで、それらの答えのいくつかは、この質問の一般的なケースについて説明しています。パーフェクト!
-dotancohen

3

comp.unix.shellのJanis Papanagnouのおかげで、次の関数を使用します。

function grep1 {
    IFS= read -r header && printf "%s\n" "$header"; grep "$@"
}

これには多くの利点があります。

  • bash、zsh、おそらくkshで動作します
  • これはgrepのドロップイン置換であるため-i、大文字と小文字を区別しないマッチング、-E拡張正規表現など、必要なフラグを引き続き使用できます。
  • プログラムで実際に一致する行があるかどうかを判断する場合、grepと同じ終了コードを常に生成します。
  • 入力が空の場合は何も出力しません

使用例:

$ ps -rcA | grep1 databases
  PID TTY           TIME CMD

$ ps -rcA | grep1 -i databases
  PID TTY           TIME CMD
62891 ??         0:00.33 com.apple.WebKit.Databases

2

別の方法gnu ed

ed -s '!ps aux' <<< $'2,$v/PATTERN/d\n,p\nq\n'

または、シェルがプロセス置換をサポートする場合:

printf '%s\n' '2,$v/PATTERN/d' ,p q | ed -s <(ps aux)

あれは:

2,$v/PATTERN/d  - remove all lines not matching pattern (ignore the header)
,p              - print the remaining lines
q               - quit

移植性が高く、gnu '!' シェル置換なし- ed組み込みのみを使用して出力をバッファrr追加ps auxし、2,$範囲内の一致しない行を削除して結果を出力します:

printf '%s\n' 'r !ps aux' '2,$v/PATTERN/d' ,p q | ed -s

そして以来sedとも受け入れ答え出力のコマンドそのものに一致するライン、sedサポートしている-f-と私は実行しますプロセス置換をサポートしていますシェル:

printf '%s\n' '2,${' '/PATTERN/!d' '}' | sed -f - <(ps aux)

これは前のedコマンドとほとんど同じことを行います。



0

それが完全なヘッダーを持つプロセスをgrepするだけの場合は、@ mrbの提案を展開します。

$ ps -f -p $(pgrep bash)
UID        PID  PPID  C STIME TTY      STAT   TIME CMD
nasha     2810  2771  0  2014 pts/6    Ss+    0:00 bash
...

pgrep bash | xargs ps -fpサブシェルなしで同じ結果が得られます。他のフォーマットが必要な場合:

$ pgrep bash | xargs ps fo uid,pid,stime,cmd -p
  UID   PID STIME CMD
    0  3599  2014 -bash
 1000  3286  2014 /bin/bash
 ...

-2

正確な行番号を知っていれば、perlを使えば簡単です!ファイルから1行目と5行目を取得したい場合は、/ etc / passwdと言います。

perl -e 'while(<>){if(++$l~~[1,5]){print}}' < /etc/passwd

他の行も取得したい場合は、その番号を配列に追加するだけです。


1
ありがとうございました。OPに従って、行のテキストの一部は知っていますが、行番号はわかりません。
-dotancohen

これは、OPに密接に関連するこのユースケースを探す際にGoogleで回答として表示されるため、ここで注目する価値があります。
ダゲエルフ

1
その場合は、新しい質問を開始し、この回答で答えることを強くお勧めします。特にあなたが言及している状況では、SEに関するあなた自身の質問に答えることは全く問題ありません。先に進み、OPに関するコメントで新しい質問にリンクします。
-dotancohen

このような質問はありますが、現在Googleには表示されません。
ダジェルフ

Dagelf、一番下の行は-あなたの答えここの質問に答えていません。@dotancohenが正しい-OPに密接に関連するこのユースケースを検索するときにGoogleでこれが回答としてポップアップする場合は、別の質問(その密接に関連するユースケースの詳細)を尋ねて回答してください。
don_crissti
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.