web-dev-qa-db-ja.com

bash:コマンドの出力を列で分割

私はこれをしたい:

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

77
flybywire

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

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

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

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

tr -s ' 'オプションは、先頭のスペースを削除しないことに注意してください。列が右揃えの場合(ps pidなど)...

$ 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コマンドにはきちんとしたことがあるため、実際にはShell functionsを簡潔に使用することができます。

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

読み取る最初のパラメーターaは、最初の列を選択し、さらにある場合は、elsethingがすべて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でのみ機能します。 DashとAshはサポートしていません。 Busyboxで文字列をコンポーネントに分割するのは本当に大変でした。単一のコンポーネントを取得して(awkを使用するなど)、必要なパラメーターごとにそれを繰り返すのは簡単です。しかし、同じ行で繰り返しawkを呼び出すか、同じ行でエコー付きの読み取りブロックを繰り返し使用することになります。効率的でもきれいでもありません。したがって、${name%% *}などを使用して分割することになります。慣れている機能の半分以上がなくなっていれば、実際にはシェルスクリプトはあまり楽しくないので、いくつかのPythonスキルに憧れます。しかし、あなたはpythonでさえそのようなシステムにインストールされないと仮定することができ、それはそうではなかった;-)。

10
Xennex81

試してみる

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

Brianeggeのawkソリューションと同様に、Perlに相当するものを次に示します。

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

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

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

2
Chris Koknat

配列変数を使用する

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

または

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

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

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

あなたの命令

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

unwindが his answer で説明しているように、tr -sを逃してスペースを絞ります。

ただし、awkを使用することもできます。これは、これらのアクションをすべて単一のコマンドで処理するためです。

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

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

0
fedorqui

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

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

0
dman

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

ps -o cmd= -p 12345

プロセスのcmmand行を取得するには、pidを指定し、他には何も指定しません。

これはPOSIXに準拠しているため、移植性があると考えられます。

0
P Shved