파일의 첫 번째 줄과 특정 줄을 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
이 답변에 필요한 복잡성은 유용성이라는 말로 측정 할 때 "한 가지 일을 잘 수행하는"유닉스 철학이 때때로 우리를 실패시키는 방법을 보여줍니다. 그리고 여전히 열 레이블을 볼 때)는 접근 방식의 단점을 보여줍니다. 때로는 상황이 매우 깨끗하게 맞지 않습니다. 그렇기 때문에 이와 같은 툴 ack이 유용하고 perl과거 sedawk, 등이 인기를 얻었던 이유는 무엇입니까 ?
iconoclast

3
물론,이 특정 예제에서는 -C인수를 사용할 ps수 있으며 grep에 파이프 할 필요가 없습니다. 예를 들어ps u -C someAppps u -C app1 -C app2 -C app3
cas

1
@iconoclast : 물론 Unixy 솔루션은 여러 줄을 다중화하여 서로 다른 필터 세트를 통해 필터링 할 수있는 도구입니다. 의 일종의 일반화 된 버전 ps aux | { head -1; grep foo; }아래 @Nahuel Fouilleul 언급 (자신이 필요한 경우 내가 그 자리에서 회수 할 수있을 것입니다 유일한 해결책은 아마 임)
거짓말 라이언

@iconoclast : 도구에 대한 경험이 부족하고 도구에 대한 지식이 있으면 도구가 실제로 잘 수행하는 것이 항상 쓸모없는 것처럼 보일 것입니다. 명령을 잘 아는 것은 사용성에 관한 마당에서 어디에도 없으며, 훌륭한 설명서를 읽고 연습하는 마당에 있습니다. 이 도구는 수십 년 동안 사용되어 왔습니다. 그들은 잘 작동하고 깨끗하게 어울립니다.
Ярослав Рахматуллин

@ ЯрославРахматуллин : 내가 말한 것을 완전히 오해했을 것 같습니다. (어쩌면 영어가 모국어가 아니기 때문일까요?) "사용성"은 유틸리티 (또는 "유용성")가 아닌 UX ( "사용자 경험")와 관련이 있습니다. 간단한 조작이 복잡 할 때 유용성 아파 지적 것은 NOT 도구는 쓸모없는 말과 동일. 분명히 그들은 쓸모가 없습니다. 그들의 올바른 마음에 아무도 쓸모 없다고 말하지 않을 것입니다.
iconoclast

답변:


67

좋은 방법

일반적으로 grep으로이 작업을 수행 할 수 없지만 다른 도구를 사용할 수 있습니다. AWK는 이미 언급되었지만 다음 sed과 같이 사용할 수도 있습니다 .

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

작동 방식 :

  1. Sed 유틸리티는 각 라인에서 개별적으로 작동하여 각각에 지정된 명령을 실행합니다. 여러 -e옵션을 지정하여 여러 명령을 가질 수 있습니다. 이 명령을 특정 행에 적용해야하는지 여부를 지정하는 범위 매개 변수를 사용하여 각 명령 앞에 붙일 수 있습니다.

  2. "1p"는 첫 번째 명령입니다. p일반적으로 모든 줄을 인쇄 하는 명령을 사용 합니다. 그러나 적용해야 할 범위를 지정하는 숫자 값 앞에 붙입니다. 여기서는 1첫 번째 줄을 의미합니다. 더 많은 줄을 인쇄하려면 첫 줄 인쇄 위치, 마지막 줄 인쇄 x,yp위치 를 사용할 수 있습니다 . 예를 들어 처음 3 줄을 인쇄하려면xy1,3p

  3. 다음 명령은 d일반적으로 버퍼에서 모든 행을 삭제합니다. 이 명령 전에 우리는 yourpattern/문자 사이에 넣습니다 . 이것은 p명령이 실행되어야하는 행을 주소 지정하는 다른 방법입니다 (먼저 명령 으로 수행 한 행을 지정하는 것이 었습니다 ). 이것은 명령이 일치하는 행에 대해서만 작동 함을 의미합니다 yourpattern. 단, 명령 !앞의 문자를 사용 d하여 논리를 반전시킵니다. 이제 지정된 패턴과 일치 하지 않는 모든 줄을 제거합니다 .

  4. 마지막으로 sed는 버퍼에 남아있는 모든 행을 인쇄합니다. 그러나 버퍼에서 일치하지 않는 행을 제거하여 일치하는 행만 인쇄합니다.

요약하자면 첫 번째 줄을 인쇄 한 다음 패턴과 일치하지 않는 모든 줄을 입력에서 삭제합니다. 라인의 나머지 (그래서 전용 라인 인쇄 패턴과 일치를).

첫 줄 문제

주석에서 언급 했듯이이 접근법에는 문제가 있습니다. 지정된 패턴이 첫 번째 줄과도 일치하면 두 번 인쇄됩니다 ( p명령에 따라 한 번, 일치 때문에 한 번). 우리는 이것을 두 가지 방법으로 피할 수 있습니다 :

  1. 다음에 1d명령 추가 1p. 이미 언급했듯이 dcommand는 버퍼에서 행을 삭제하고 번호 범위로 범위를 지정합니다. 즉, 첫 번째 행만 삭제합니다. 따라서 명령은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 sedbusybox구현.

미친 길

그러나 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지정하고 인쇄하려는 필드 (또는 열)를 지정합니다. 따라서 모든 :문자의 각 줄을 잘라 두 번째 및 모든 후속 열만 인쇄 하려고합니다 . 이렇게하면 구분 기호가있는 첫 번째 열이 효과적으로 제거되며 이것이 정확히 필요한 것입니다.


4
라인 넘버링도 가능하며 cat -n, grep이 남용되는 것처럼 더 명확 해 보일 것입니다.
Alfe

1
nl빈 줄을 세지 않고 (줄 번호없이 인쇄), cat -n선행 공백으로 번호 매기기를 포맷하고, grep -n .빈 줄을 없애고 콜론을 추가합니다. 모두 ... 자신의 ... 기능을 가지고 ;-)
Alfe

2
매우 교육적이고 잘 작성된 답변. "Pretend"(처음 근처)를 "Prepend"로 바꾸려고했지만 더 많은 변경이 필요했고 게시물에서 임의의 크랩을 바꾸고 싶지 않은 경우 문제를 해결하고 싶을 수도 있습니다.
Bill K

2
ps aux | sed '1p;/pattern/!d'pattern 과 일치하면 첫 번째 줄을 두 번 인쇄합니다 . 가장 좋은 방법은 다음 b명령 을 사용하는 것 ps aux | sed -e 1b -e '/pattern/!d'입니다.. cat -nPOSIX가 아닙니다. grep -n '^'모든 줄에 번호를 매 깁니다 (빈 줄이없는 ps 출력에는 문제가되지 않습니다). nl -ba -d $'\n'모든 줄에 번호를 매 깁니다.
Stéphane Chazelas

2
그 주 1b;..."B"다음에 다른 명령이있을 수없고, 휴대용이나 POSIX되지 않습니다, 그래서 당신은 개행 또는 다른 -e 표현을해야합니다.
Stéphane Chazelas

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

나는 좀 어색한 것을 배울 필요가있다. 아주 좋아요
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; ... }헤더가 공백으로 시작하는 경우에 대비하여 사용할 수 있습니다.
Alfe

이것은 정확하게 문제를 직접 공격합니다. 좋은!
도탄 코프

3
head -1읽기 / 에코 콤보 대신에 사용하고 싶습니다 .
chepner

1
글쎄, 그것은 head -n1내 bash에서 작동합니다 . 아마도 구현에 따라 다를 수 있습니다. 이 경우 내 머리는 전체 입력을 읽지 않고 첫 번째 줄만 읽으며 나머지는 입력 버퍼에 남겨 둡니다.
크 르지 스토 프 아담 스키

2
head -n1POSIX 사양조차도 입력이 얼마나 허용되는지에 대해서는 침묵하는 것처럼 보이므로 read line; echo $line결국 더 이식성이 좋습니다.
chepner

14

PS는 내부 필터를 지원합니다.

bash 프로세스를 찾고 있다고 가정하십시오.

ps -C bash -f

명명 된 모든 프로세스를 나열합니다 bash .


고마워요. 그러나 파이썬에서 시작된 스크립트는 무엇보다도 찾을 수 없습니다.
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
나는 아직 보지 못했지만, 신뢰성을 향상하는 방법 중 하나는 전에 파이프 라인에 약간의 지연을 삽입하는 것입니다 grep: | { sleep .5; cat }.
Thor

2
동시성 문제를 피하기 위해 절전 모드를 추가하는 것은 항상 해킹입니다. 이것이 효과가있을 수 있지만, 그것은 어두운쪽으로 나아가는 단계입니다. 이를 위해 -1입니다.
Alfe

1
이 답변을 시도하는 동안 몇 가지 다른 이상한 문제가 발생 했습니다. 확인할 질문을
Rqomey

이것은 tee의 흥미로운 사용법이지만 신뢰할 수 없으며 종종 출력 줄만 인쇄하지만 헤더 줄은 인쇄하지 않습니다.
dotancohen

4

아마도 두 가지 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
나는 첫 번째와 두 번째 ps aux호출 사이에서 상황이 바뀔 수 있기 때문에이 솔루션을 좋아하지 않습니다 ... 정적 첫 줄을 원한다면 왜 수동으로 에코하지 않습니까?
Shadur

1
상황 에서 두 통화 간의 변경은 방해되지 않습니다 . 첫 번째는 두 번째 출력에 항상 맞는 헤드 라인 만 제공합니다.
Alfe

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


고마워요. 그러나 파이썬에서 시작된 스크립트는 무엇보다도 찾을 수 없습니다.
dotancohen

4

테스트를 위해 다음을 .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을 대신 넣으면)를 제공해야합니다.

source ~/.bashrc

이 기능은 쉘 명령 행에서 자동 완성됩니다. 다른 답변에서 언급했듯이 첫 번째 줄을 파일로 파이프하여 한 호출을 ps에 저장할 수 있습니다.


1
좋아, 몇 년 동안 그런 종류의 기능을 사용해 왔습니다. 내 버전을psl 호출합니다 .이 버전 은 한 번만 호출 ps하고 grep(필요하지 않음 head).
Adam Katz

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내장을 r위해 r출력을 EAD ps aux버퍼로 다음에 일치하지 않는 행을 삭제 2,$범위 및 그 결과를 인쇄 :

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

그리고 이후 sed허용 대답 출력의 명령 또한 라인은와 자신을 일치 sed지원하는 -f-I가 실행됩니다 공정 대체를 지원하고 쉘 :

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

이전 ed명령 과 거의 같은 기능을 합니다.


1

펄 방식 :

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

sed원하지 않는 줄을 선택할 위험이없는 것보다 빠르고 읽기 쉽습니다 .



0

그것이 전체 헤더가있는 프로세스를 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
 ...

-2

정확한 줄 번호를 알고 있다면 펄로 쉽게 할 수 있습니다! 파일에서 1과 5 행을 가져 오려면 / etc / passwd라고 말하십시오.

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

다른 줄도 얻으려면 배열에 숫자를 추가하십시오.


1
감사합니다. OP에 따라 줄 번호가 아닌 줄의 일부 텍스트를 알고 있습니다.
dotancohen

OP와 밀접한 관련이있는 사용 사례를 찾을 때 Google에서 답변으로 표시되므로 여기에 주목할 가치가 있습니다.
Dagelf

1
이 경우 새로운 질문을 시작하여이 답변으로 답변 할 것을 적극 권장합니다. SE에 대해, 특히 언급 한 상황에서 자신의 질문에 대답하는 것이 좋습니다. OP에 대한 의견으로 새 질문에 연결하십시오.
dotancohen

이러한 질문이 있지만 현재 Google에 나타나지 않습니다.
Dagelf

Dagelf의 결론은 귀하의 답변 여기의 질문에 대한 답변 이 아닙니다 . @dotancohen이 맞습니다 . OP와 밀접한 관련이있는 사용 사례를 찾을 때 Google에서 답변으로 표시되는 경우 밀접한 관련 사용 사례를 자세히 설명하는 별도의 질문을하고 답변하십시오.
don_crissti
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.