web-dev-qa-db-ja.com

ファイルの最初の行と特定の行をgrepする方法

次のような単純な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"ファイル記述子 の良いユースケースと思われます。

77
dotancohen

良い方法

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

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

使い方:

  1. Sedユーティリティは、各行で個別に動作し、それらのそれぞれで指定されたコマンドを実行します。複数の-eオプションを指定して、複数のコマンドを使用できます。各コマンドの前に、このコマンドを特定の行に適用する必要があるかどうかを指定する範囲パラメーターを付けることができます。

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

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

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

要約すると、最初の行を出力してから、パターンに一致しないすべての行を入力から削除します。残りの行が出力されます(つまり、doがパターンに一致する行のみ)。

最初の行の問題

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

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

  2. 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'

使い方

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

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

行番号の問題

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

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

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

68

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
58
mrb
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
31

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

Bashプロセスを探しているとします。

ps -C bash -f

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

14
daisy

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

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

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

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

括弧で囲まれた部分は、一般的な使用のために独自のスクリプトに入れることができます。

出力を(sortなどに)パイプすることができ、ヘッダーが上部に残るという便利な点もあります。

6
antak

teeおよびheadを使用することもできます。

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

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

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

出力順序は保証されません にも注意してください。

5
Thor

おそらく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
4
emcconville

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

4
harp

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

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

使用法:psls [grep pattern]

$ 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を配置する場合は.bash_profile)を入手してください。

source ~/.bashrc

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

4
taco

並べ替えますが、ヘッダー行を上部に保持します

# 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
3
Mikel

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
3
bdesham

Perlの方法:

ps aux | Perl -ne 'print if /pattern/ || $.==1'

sedよりも読みやすく、高速で、不要な行を選択するリスクがありません。

1
emazep

それが完全なヘッダーを持つgreppingプロセスのみの場合は、@ 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
 ...
0
user86969