Bash를 사용하여 열별로 명령 출력을 분할 하시겠습니까?


87

나는 이것을하고 싶다 :

  1. 명령을 실행하다
  2. 출력 캡처
  3. 라인을 선택
  4. 해당 행의 열을 선택

예를 들어, a에서 명령 이름을 얻고 싶다고 가정 해 봅시다 $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두 번째 열과 세 번째 열 사이에 공백 을 추가하여 테이블의 유사성을 유지 cut하면서 빈 문자열을 선택한다는 것입니다. 물론 cut4 번째 필드가 아닌 7 번째 필드를 선택하는 데 사용할 수 있지만, 특히 출력이 가변적이고 미리 알 수없는 경우 어떻게 알 수 있습니까?


2
awk (및 25 자 이상)를 사용하십시오.
Michael Foukarakis

답변:


178

한 가지 쉬운 방법은 tr반복되는 필드 구분 기호를 짜내 는 패스를 추가하는 것입니다.

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

1
나는 이것을 좋아하고, tr보다 가벼워 보인다awk
flybywire

3
나는 동의하는 경향이 있지만 그것은 또한 내가 awk를 배우지 않았기 때문일 수 있습니다. :)
언 와인드

서브 트링으로 관심있는 PID가 포함 된 PID 프로세스가있는 경우 작동하지 않습니다.
David Grayson 2012

1
또한 일부 PID :가 왼쪽에 공백이있는 반면 다른 PID는 그렇지 않은 경우 필드 번호가 꺼집니다.
tripleee

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

는 IS ;에서이 { print $4; }필요? 이 리눅스에 나에게 아무런 영향이없는 것으로 그것의 목적으로 그냥 궁금 보인다 제거
igniteflow

@igniteflow는 print 문을 지나서 계속 추가하려는 경우 명령의 끝을 나타내지 않습니까?
joshmcode

16

tr -s ' '옵션은 단일 선행 공백을 제거하지 않습니다. 열이 오른쪽 정렬 된 경우 ( 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

그런 다음 세 번째 열을 출력합니다. 내 의견에 표시된대로 ...

파이프 된 읽기는 호출 스크립트에 변수를 전달하지 않는 환경에서 실행됩니다.

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를 호출하거나 동일한 라인에서 echo가있는 읽기 블록을 반복적으로 사용합니다. 효율적이거나 예쁘지 않습니다. 그래서 당신은 ${name%% *}등등. 익숙한 기능의 절반 이상이 사라지면 실제로 쉘 스크립팅이 더 이상 재미 있지 않기 때문에 일부 Python 기술을 갈망하게 만듭니다. 그러나 파이썬조차도 그러한 시스템에 설치되지 않을 것이며 ;-)가 아니라고 가정 할 수 있습니다.


당신은에서 변수 따옴표를 사용해야 echo "$a"하고 echo "$c"있지만.
tripleee

모든 파이프 블록이 자체 서브 셸 또는 프로세스에서 실행되고 주변 블록에 변수를 반환 할 수없는 것처럼 보입니다. 에코 후 출력을 얻을 수 있지만. var=$(....... | { read a b c d; echo $c; }). 이는 단일 (문자열)에서만 작동하지만 Bash에서는 다음을 사용하여 배열로 분할 할 수 있습니다.ar=($var)
Xennex81

@tripleee 나는 그것이 프로세스의 그러한 단계에서 문제라고 생각하지 않습니다. 그것이 필요한지 아닌지 곧 알게 될 것이며, 어느 시점에서 그것이 깨지면 학습 교훈입니다. 그리고 그 큰 따옴표를 사용해야하는 이유 를 알 있습니다 ;-). 그리고 그것은 더 이상 당신이 다른 사람들로부터들은 말이 아닙니다. 불장난! :디. :피.
Xennex81 2015 년

정교 답 : D
ncomputers

이것은 내가 그렇게 말하지 않기에는 너무 도움이 된 대답이었습니다.
Ivan X

4

시험

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

1
@flybywire-이 간단한 예제에서는 과도 할 수 있지만 선택한 데이터에 대해 더 복잡한 처리를 수행해야하는 경우이 관용구가 훌륭합니다.
James Anderson

또한 요즘 기본 스크립팅 셸은 일반적으로 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 솔루션에 감사드립니다-autosplit에 대해 몰랐고 여전히 perl이 다른 도구를 끝내는 도구라고 생각합니다 ..;).
Gerard ONeill 2015 년

1

올바른 줄 (6 번 줄의 예)을 얻는 것은 head와 tail로 이루어지며 올바른 단어 (단어 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 *
데이비드 Z

1

당신의 명령

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

tr -sunwind가 그의 대답 에서 설명하는 것처럼 공간을 짜기 위해 a 를 놓칩니다 .

그러나 awk하나의 명령으로 이러한 모든 작업을 처리하므로을 사용할 수 있습니다 .

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

이 포함 된 행의 네 번째 열을 인쇄합니다 11383. 11383줄의 시작 부분에 나타날 경우 일치 시키려면 라고 말할 수 있습니다 ps | awk '/^11383/ {print $4}'.


0

이러한 모든 greps 및 작업을 수행하는 대신 출력 형식을 변경하는 ps 기능을 사용하는 것이 좋습니다.

ps -o cmd= -p 12345

pid가 지정된 프로세스의 cmmand 행을 얻습니다.

이것은 POSIX를 준수하므로 휴대용으로 간주 될 수 있습니다.


1
flybywire는 그가 ps를 예로 사용하고 있다고 말하며 질문은 그보다 더 일반적입니다.
Ogre Psalm33 2013-04-30

0

Bash set는 모든 출력을 위치 매개 변수로 구문 분석합니다.

예를 들어 set $(free -h)명령을 사용 echo $7하면 "Mem :"이 표시됩니다.


이 방법은 명령에 한 줄의 출력이있는 경우에만 유용합니다. 충분히 일반적이지 않습니다.
codeforester

사실이 아닙니다. 모든 출력은 행에 관계없이 위치 매개 변수에 배치됩니다. ex set $(sar -r 1 1); echo "${23}"
dman

내 요점은 출력이 방대하고 필드가 많은 경우 인수의 위치를 ​​결정하기 어렵다는 것입니다. awk그것에 대해 최선의 방법입니다.
codeforester 2017 년

이것은 또 다른 해결책입니다. OP는이 단일 사용 사례에 대해 awk 언어를 배우고 싶지 않을 수 있습니다. 태그는 상태 bash가 아니라 awk.
dman
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.