Bashを使用してコマンドの出力を列ごとに分割しますか?


87

私はこれをしたい:

  1. コマンドを実行する
  2. 出力をキャプチャします
  3. 行を選択します
  4. その行の列を選択します

例として、からコマンド名を取得したいとします$PID(これは単なる例であり、これがプロセスIDからコマンド名を取得する最も簡単な方法であることを示唆しているわけではないことに注意してください-私の本当の問題は出力形式を制御できない別のコマンド)。

実行するpsと、次のようになります。


  PID TTY          TIME CMD
11383 pts/1    00:00:00 bash
11771 pts/1    00:00:00 ps

今、私はやってps | egrep 11383

11383 pts/1    00:00:00 bash

次のステップ:ps | egrep 11383 | cut -d" " -f 4。出力は次のとおりです。

<absolutely nothing/>

問題はcut、出力を1つのスペースでカットしps、2列目と3列目の間にスペースを追加してテーブルの類似性を維持cutするときに、空の文字列を選択することです。もちろん、cut4番目のフィールドではなく7番目のフィールドを選択するために使用することもできますが、特に出力が可変で事前に不明な場合は、どうすればわかりますか。


2
awk(およびさらに25文字)を使用します。
Michael Foukarakis 2009年

回答:


178

簡単な方法の1つは、パスを追加して、tr繰り返されるフィールドセパレーターを絞り出すことです。

$ ps | egrep 11383 | tr -s ' ' | cut -d ' ' -f 4

1
私はこれが好きtrです、より軽量のように見えますawk
flybywire 2009年

3
私は同意する傾向がありますが、それは私がawkを学んでいないためかもしれません。:)
くつろぐ

サブトリングとして関心のあるPIDを含むPIDを持つプロセスがある場合は機能しません。
デビッドグレイソン2012年

1
また、一部のPIDが左側にスペースが埋め込まれている場合とそうでない場合は、フィールド番号がオフになります。
トリプリー2015年

68

最も簡単な方法はawkを使用することだと思います。例:

$ echo "11383 pts/1    00:00:00 bash" | awk '{ print $4; }'
bash

4
元の質問との互換性のため、ps | awk "\$1==$PID{print\$4}"または(より良い)ps | awk -v"PID=$PID" '$1=PID{print$4}'。もちろん、Linuxでは、単純にxargs -0n1 </proc/$PID/cmdline | head -n1またはを実行できますがreadlink /proc/$PID/exe、とにかく...
ephemient 2009年

;では{ print $4; }必要?それを削除しても、Linuxでは効果がないようですが、その目的に興味があります
igniteflow 2016

@igniteflowは、printステートメントを超えて追加を続けたい場合、コマンドの終了を示しませんか?
joshmcode

16

このtr -s ' 'オプションでは、先頭のスペースが1つも削除されないことに注意してください。列が右揃えの場合(pspidの場合のように)...

$ ps h -o pid,user -C ssh,sshd | tr -s " "
 1543 root
19645 root
19731 root

次に、最初の列である場合、切り取ると、これらのフィールドの一部に空白行が表示されます。

$ <previous command> | cut -d ' ' -f1

19645
19731

スペースを前に付けない限り、明らかに

$ <command> | sed -e "s/.*/ &/" | tr -s " "

さて、(名前ではなく)pid番号のこの特定のケースでは、次のような関数がありますpgrep

$ pgrep ssh


シェル関数

ただし、コマンドには次のような優れた点があるため、一般に、シェル関数を簡潔に使用することは実際には可能ですread

$ <command> | while read a b; do echo $a; done

読み取る最初のパラメーターaは、最初の列を選択し、それ以上ある場合は、他のすべてがに配置されbます。その結果、列の数+1より多くの変数が必要になることはありません。

そう、

while read a b c d; do echo $c; done

次に、3番目の列を出力します。私のコメントに示されているように...

パイプ読み取りは、呼び出し元のスクリプトに変数を渡さない環境で実行されます。

out=$(ps whatever | { read a b c d; echo $c; })

arr=($(ps whatever | { read a b c d; echo $c $b; }))
echo ${arr[1]}     # will output 'b'`


アレイソリューション

したがって、@ frayserによる答えは、デフォルトでスペースに設定されているシェル変数IFSを使用して、文字列を配列に分割することです。ただし、Bashでのみ機能します。ダッシュとアッシュはそれをサポートしていません。Busyboxで文字列をコンポーネントに分割するのに本当に苦労しました。単一のコンポーネントを取得して(たとえばawkを使用して)、必要なすべてのパラメーターに対してそれを繰り返すのは簡単です。しかし、同じ行でawkを繰り返し呼び出すか、同じ行でechoを使用して読み取りブロックを繰り返し使用することになります。これは効率的でもきれいでもありません。したがって、を使用して分割することになります ${name%% *}等々。慣れ親しんだ機能の半分以上がなくなった場合、実際にはシェルスクリプトはそれほど楽しくないため、Pythonのスキルに憧れます。しかし、Pythonでさえそのようなシステムにインストールされないと想定することができ、そうではありませんでした;-)。


ただし、変数の前後には引用符を使用する必要がecho "$a"ありecho "$c"ます。
トリプリー2015

パイプされたすべてのブロックが独自のサブシェルまたはプロセスで実行され、変数を囲んでいるブロックに返すことができないように見えますか?あなたはそれをエコーし​​た後にその出力を得ることができますが。var=$(....... | { read a b c d; echo $c; })。これは単一の(文字列)に対してのみ機能しますが、Bashでは、次を使用して配列に分割できますar=($var)
Xennex81 2015年

@tripleeeそれはプロセスのそのような段階では問題ではないと思います。それが必要かどうかはすぐにわかります。ある時点でそれが壊れた場合、それは学習レッスンです。そして、なぜそれらの二重引用符を使用しなければならなかったのがわかります;-)。そして、それはもはやあなたが他の人から言うのを聞いたことのないものです。火遊び!:D。:p。
xennex81 2015年

入念な回答:D
ncomputers 2017年

これは私にとってあまりにも役に立った答えであり、そうは言わなかった。
Ivan X

4

試してみてください

ps |&
while read -p first second third fourth etc ; do
   if [[ $first == '11383' ]]
   then
       echo got: $fourth
   fi       
done

1
@ flybywire-この単純な例ではやり過ぎかもしれませんが、選択したデータに対してより複雑な処理を行う必要がある場合は、このイディオムが優れています。
ジェームズアンダーソン

また、最近のデフォルトのスクリプトシェルは通常bashではないことに注意してください。
デビッドギブン

2

配列変数の使用

set $(ps | egrep "^11383 "); echo $4

または

A=( $(ps | egrep "^11383 ") ) ; echo ${A[3]}

2

brianeggeのawkソリューションと同様に、Perlに相当するものは次のとおりです。

ps | egrep 11383 | perl -lane 'print $F[3]'

-a@F配列に列データを入力する自動分割モードを有効にします。データがスペース区切りではなくコンマ区切りの場合に
使用し-F,ます。

Perlは1ではなく0からカウントを開始するため、フィールド3が出力されます。


1
あなたのperlソリューションをありがとう-自動分割について知りませんでした、そしてそれでもperlは他のツールを終わらせるためのツールであると思います..;)。
Gerard ONeill 2015

1

正しい行(行番号6の例)の取得は頭と尾で行われ、正しい単語(単語番号4)はawkでキャプチャできます。

command|head -n 6|tail -n 1|awk '{print $4}'

awkが行で選択できることを将来の読者に注意してください:awk NR=6 {print $4}もう少し効率的です
David Z

1
もちろん、私はawk NR==6 {print $4}* doh *を意味しました
David Z

1

あなたの命令

ps | egrep 11383 | cut -d" " -f 4

tr -sunwindが彼の答えで説明しているようにスペースを絞るのを逃します

しかし、あなたは多分使いたいでしょう awkこれらすべてのアクションを1つのコマンドで処理、。

ps | awk '/11383/ {print $4}'

これにより、を含む行の4番目の列が出力され11383ます。11383行の先頭に表示されている場合にこれを一致させたい場合は、と言うことができますps | awk '/^11383/ {print $4}'


0

これらすべてのグリップなどを行う代わりに、出力形式を変更するps機能を使用することをお勧めします。

ps -o cmd= -p 12345

pidが指定されたプロセスのcmmand行を取得します。

これはPOSIXに準拠しているため、ポータブルと見なすことができます。


1
flybywireは、彼が例としてpsを使用していると述べていますが、質問はそれよりも一般的です。
鬼詩篇33 2013

0

Bashsetは、すべての出力を位置パラメーターに解析します。

たとえば、set $(free -h)コマンドを使用echo $7すると、「Mem:」と表示されます。


この方法は、コマンドに1行の出力がある場合にのみ役立ちます。十分に一般的ではありません。
codeforester 2017年

それは真実ではありません。すべての出力は、行に関係なく位置パラメータに配置されます。例set $(sar -r 1 1); echo "${23}"
dman 2017年

私のポイントは、出力が大量で多くのフィールドがある場合、引数の位置を決定するのは難しいということでした。 awkそれについて行くための最良の方法です。
codeforester 2017年

これは単なる別の解決策です。OPは、この単一のユースケースでawk言語を学習したくない場合があります。タグは状態bashを示しますが、状態は示しませんawk
dman 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.