추가 읽기

추가 읽기

ps -o command공백으로 구분되고 따옴표가 없는 인수를 사용하여 각 명령을 별도의 줄에 표시합니다.

$ ps -o command
COMMAND
bash
ps -o command

이로 인해 참조가 올바른지 확인하거나 명령을 복사하여 붙여넣어 다시 실행할 때 문제가 발생할 수 있습니다. 예를 들어:

$ xss-lock --notifier="notify-send -- 'foo bar'" slock &
[1] 20172
$ ps -o command | grep [x]ss-lock
xss-lock --notifier=notify-send -- 'foo bar' slock

의 출력은 ps오해의 소지가 있습니다. 복사하여 붙여넣으려고 하면 명령이 원래 명령과 동일한 작업을 수행하지 않습니다. 그래서printf %q실행 중인 명령 목록을 인쇄하는 Bash와 같은 방법이 있습니까 ?매개변수를 올바르게 이스케이프하거나 인용하십시오.?

답변1

/proc/$pid/cmdlineLinux에서는 프로세스 ID가 지정된 명령에서 약간 더 원시적인 인수 목록을 얻을 수 있습니다. args는 null 문자로 구분됩니다. 귀하의 경우에는 cat -v /proc/$pid/cmdlinenul 을 다음과 같이 처리해 보십시오 .^@xss-lock^@--notifier=notify-send -- 'foo bar'^@slock^@

다음 Perl 스크립트는 proc 파일을 읽고 nul을 줄 바꿈 및 탭으로 바꿀 수 있습니다. 예를 들면 다음과 같습니다.

xss-lock
    --notifier=notify-send -- 'foo bar'
    slock

또는 다음과 같이 다시 인용된 명령을 얻을 수 있습니다.

xss-lock '--notifier=notify-send -- '\''foo bar'\''' 'slock' 

if(1)다음 으로 바꾸면 if(0):

perl -e '
  $_ = <STDIN>;
  if(1){
      s/\000/\n\t/g;  
      s/\t$//; # remove last added tab
  }else{
      s/'\''/'\''\\'\'\''/g;  # escape all single quotes
      s/\000/ '\''/;          # do first nul
      s/(.*)\000/\1'\''/;     # do last nul
      s/\000/'"' '"'/g;       # all other nuls
  }
  print "$_\n";
' </proc/$pid/cmdline 

답변2

/proc/PID/cmdlinemeuh가 지적했듯이 NUL 바이트로 구분된 인수를 통해 Linux 및 NetBSD에서 문자열을 얻을 수 있습니다. 실행 가능한 명령줄로 변환하는 빠르고 더러운 방법은 다음과 같습니다.

perl -ne 'print join(" ", map quotemeta, split(/\000/)), "\n"' /proc/.../cmdline

출력은 다음과 같습니다.

xss\-lock \-\-notifier\=notify\-send\ \-\-\ \'foo\ bar\' slock

셸에 직접 복사하여 붙여넣어 실행할 수 있습니다.

더 짧은 변형(Perl 5.10 이상 필요):

perl -nE '$, = " "; say map quotemeta, split /\0/' /proc/.../cmdline

이렇게 하면 골프 버전(40바이트)은 다음과 같습니다.

perl -nE'$,=" ";say map"\Q$_",split/\0/' /proc/.../cmdline

답변3

다른 답변과 마찬가지로 여기에 핵심이 있습니다. 이는 Linux, FreeBSD 및 NetBSD에서 사용할 수 있습니다./proc/${PID}/cmdline

perl다소 정교한 대안 과 마찬가지로 , 종료된 문자열은 일반적이지만 비표준 형식 지정자를 사용하여 명령 인수로 쉽게 변환될 수 있고 (질문에서 설명했듯이) 명령은 일반적이지만 비표준 형식 지정자를 사용하여 쉽게 변환될 수 있다는 sed점을 관찰 하세요 . 쉘 인용 문자열로 변환됩니다.xargs-0printf%q

i*/의 경우 /proc/[0-9]
하다
    기본 이름 "$i"
    < $i/cmdline xargs -0 printf '\t%q'
    printf '\n'
완벽한

유일하게 까다로운 부분은 FreeBSD와 같은 시스템의 경우 시스템 외부 printfprintf내장 . sh이는 %q의 인기보다 낮습니다. 그러므로 사람들은 다른 사람의 것을 이용해야 합니다.-0xargsprintf

i*/의 경우 /proc/[0-9]
하다
    기본 이름 "$i"
    < $i/cmdline xargs -0 zsh -c 'printf "\t%q" "$0" "$@"'
    printf '\n'
완벽한

프로그램 시작 시 사용되는 명령 이름 및 매개변수가 보장되지는 않습니다. 프로그램은 여기에 표시되는 내용을 변경할 수 있습니다. 많은 사람들이 이 작업을 수행하며 setproctitle()인수 문자열을 엉망으로 만드는 잘못된 함수를 사용한다는 것을 알게 될 것입니다. 당신이 할 수 있는 일은 아무것도 없습니다. 각 프로그램 자체잘못된 매개변수 문자열을 구성하여 모든 것을 하나의 매개변수에 넣었습니다.

추가 읽기

답변4

나는 다음의 모든 것이 표준 PC Linux 배포판이라고 가정한다는 점부터 시작하겠습니다. 예를 들어 테스트할 때 기본 아치 Linux 설치를 어느 정도 사용했습니다.


ps -ocommand | grep \[x]ss-lock

...먼저 실행 중인 프로세스 명령줄 목록을 인쇄한 다음 정규식과 일치하는 항목으로만 목록을 필터링합니다.\[x]ss-lock. 이것\[x]...정규식은 다음과 같은 여러 이름을 나열하는 목록을 처리하기 위한 매우 일반적인 해결 방법으로 사용됩니다.xss-lock단어가 포함된 명령을 사용하여 다른 목록 필터링xss-lock.

그러나 이것이 최선의 방법은 아닙니다. 리눅스 시스템 ps은 일반적으로procps-ng ps이는 -C내가 아는 한 AIX의 ps.

어쨌든, 경기의 일부를 포기할 수 있습니다. 예를 들면 다음과 같습니다.

ps -C xss-lock -ocommand

...명령 이름이 실행 중인 프로세스의 명령줄만 인쇄하는 경우(당신은 또한 그들을 나열할 수 있습니다 ps -Aocomm=)정확히 일치 xss-lock- grep -Fx필터와 같습니다. 도착하다실제로정규식으로 필터링 pgrep유사한 경쟁 없는 정규식 일치를 위한 도구도 있습니다.


같은 이름,procps-ng ps구문 분석 트리의 파일을 통해 작동합니다 /proc. ps -ocommand예를 들어, /proc/$pid/cmdline인쇄해야 할 각 항목에 대한 파일을 읽고 교체합니다.\0영점에스(마지막 것이 없으면 Ewline이 됩니다 \n)~에(파일의 각 매개변수를 분리합니다)각각 공간이 있습니다.

쉘 인용 인수 목록을 원하면 비슷한 작업을 수행해야 합니다. 목록을 인용하는 가장 간단한 방법은 '하드 따옴표를 사용하는 것입니다. 하드 참조 문자열은 항상 단순하기 때문에 가장 간단합니다. 하드 참조 문자열은 다른 하드 참조를 포함할 수 없기 때문에 구문 분석 시 깊은 재귀가 없습니다. 그래서...

'it'\''s not a single string'

...단일 하드 참조 문자열이 아니라 세 개의 연결된 문자열입니다. 첫 번째,it양쪽은 큰 따옴표로 둘러싸여 있습니다. 두 번째는 아포스트로피로 묶인 단일 백스페이스입니다.단일 문자열이 아닙니다., 첫 번째 항목처럼 하드 참조됩니다. 쉘은 인용된 세 문자열을 모두 하나의 쉘로 결합합니다.단어파싱할 때.

파일에 대해서도 동일한 작업을 수행할 수 있습니다 /proc/$pid/cmdline. 각 파일을 다음과 같이 분할해야 합니다.\0영점바이트 안에 있는 각 아포스트로피를 앞에 붙입니다.'\''을 선택한 다음 각 개체를 아포스트로피로 묶고 \t각 개체 사이에 하나 이상의 ab 또는 공백 문자를 삽입합니다.

지금 생각해보면 첫 번째 버전은 불필요하게 복잡했고 실제로 오류가 발생하기 쉬웠습니다. 직관적으로 나는 각 명령의 내용을 하드 인용하려고 노력했습니다.논쟁- 따라서 단일 객체의 모든 객체에서 /proc/$pid/file첫 번째 객체를 뺀 것입니다. 그러나 이것은 작동하지 않습니다.(실제로 전체 결과 명령줄의 참조 레벨을 완전히 반전시킵니다.)명령 이름에 홀수 개의 아포스트로피가 포함된 경우. 어쨌든 상관없습니다. 명령줄에서 실행할 때도 잘 작동합니다 'cat'. cat실제로 그것은 작동합니다더 나은 것쉘 별칭을 해석할 수 있으면 첫 번째 별칭을 cat다른 곳보다 찾을 가능성이 더 높기 때문입니다./proc/$pid/file(무슨 일이 일어났던 것처럼 cat).


그래서 meuh의 perl 스크립트와 비슷한 것을 만들었지만 GNU 도구를 사용했습니다. 특히 이 sed -z옵션은 GNU에게 sed입력을 다음으로 분할하도록 지시합니다.\0영점ewlines 대신 bytes \n, -s각 파일 인수가 별도의 입력 스트림으로 처리됨을 나타내는 옵션 포함(그래서 $마지막 라인은 각 매개변수에 대해 별도로 참조될 수 있으며, H각 매개변수에 대해 이전 공간이 다시 초기화됩니다.). 명령 이름과 일치하는 실행 중인 각 프로세스에 대해 ps -Csh -opid=pid 번호가 한 줄에 인쇄 sh되고 sed "s|$|/cmdline|"경로 이름이 각 프로세스에 추가됩니다.

때문에 ,$IFSunset$(cmdsub's)출력은 쉘에 의해 공백의 개별 인수로 확실히 분할되며( \n경로 이름당 하나의 ewline) 프로세스는 sed -sze이를 찾을 때 존재했던 모든 경로 이름에 대한 인수 목록을 가져옵니다. 이 마지막 비트는 읽으려고 할 때 인쇄되는 오류 메시지에서 볼 수 있듯이 전체 레이싱 게임을 다시 소개합니다./proc/$pid/cmdlineps -Csed -szecmdline명령 하위 프로세스에 대한 파일은 sh더 이상 존재하지 않습니다. sed -sze호출될 때 이미 종료되었기 때문입니다.


sh -c '
    cd /proc; unset IFS
    sed  -sze "H;1h;$"\!"d;x"  \
         -e   "s/$1/&\\\\&&/g" \
         -e   "s/\x00/$1 $1/g" \
         -e   "s/.*/$1&$1\n/"  \
    $(  ps  -Csh -opid=  | 
        sed "s|$|/cmdline|")   /dev/null
' -- \'

'./sh' '-IE'
'sh'
'./sh' '-E'
'../sh'
'sh' '-c' '
        cd /proc; unset IFS
        sed  -sze "H;1h;$"\!"d;x"  \
             -e   "s/$1/&\\\\&&/g" \
             -e   "s/\x00/$1 $1/g" \
             -e   "s/.*/$1&$1\n/"  \
        $(  ps  -Csh -opid=  |
            sed "s|$|/cmdline|")   /dev/null
' '--' ''\'''

...먼저 테스트와 시연 목적으로 삽입된 참조 문제를 겪었습니다 sh -c. 테스트를 실행할 때 많은 수의 간격 인수가 있는 프로시저가 필요했습니다.

마지막 빈 입력 줄을 제거하고 다음에 인용된 명령을 종료하면/dev/null sed -sze( 프로세스가 ps -Csome_process결과 없이 표준 입력을 하이재킹하는 것을 방지하는 데 사용됨 ), sh -c프로세스dash sh( 다음이 포함된 시스템의 경우exec sed -sze다음 입력 줄을 확인하기를 기다리는 대신 그 자체로 대체 됩니다 . 이 경우 PID를 유지하므로 sed -sze자체 파일을 읽을 수 있습니다 ./proc/$pid/cmdlinesh -c

sh -c '
    cd /proc; unset IFS
    sed  -sze "H;1h;$"\!"d;x"  \
         -e   "s/$1/&\\\\&&/g" \
         -e   "s/\x00/$1 $1/g" \
         -e   "s/.*/$1&$1\n/"  \
    $(  ps  -Csh -opid=  | 
        sed "s|$|/cmdline|")   /dev/null' -- \'

'./sh' '-IE'
'sh'
'./sh' '-E'
'../sh'
'sed' '-sze' 'H;1h;$!d;x' '-e' 's/'\''/&\\&&/g' '-e' 's/\x00/'\'' '\''/g' '-e' 's/.*/'\''&'\''\n/' '2508/cmdline' '3773/cmdline' '5099/cmdline' '26599/cmdline' '31487/cmdline' '31488/cmdline' '31881/cmdline' '/dev/null'
sed: can't read 31488/cmdline: No such file or directory
'sh'

다음은 비슷한 버전이지만 전체 명령을 개별적으로 인용하고 다른 모든 레이어에 이스케이프된 하드 참조를 쌓습니다.

eval "set $(
    sh -c '
        cd /proc; unset IFS
        sed  -sze "H;$"\!"d;x"                  \
             -e   "s/$1/$2\\\\$2$2/g"           \
             -e   "s/\x00\([^\x00]*\)/$2\1$2 /g"\
             -e   "s/.*/$1&$1\\\\\n /"          \
        $(  ps  -Csh -opid=  |
            sed "s|$|/cmdline|")    /dev/null
    ' -- \' "'\\\''")"
i=
for arg do printf "$arg $((i+=1)):\t%s\n" "$arg"
done;   eval "$5"

arg 1:  './sh' '-IE' 
arg 2:  'sh' 
arg 3:  './sh' '-E' 
arg 4:  '../sh' 
arg 5:  'sh' '-c' '
        cd /proc; unset IFS
        sed  -sze "H;$"\!"d;x"                  \
             -e   "s/$1/$2\\\\$2$2/g"           \
             -e   "s/\x00\([^\x00]*\)/$2\1$2 /g"\
             -e   "s/.*/$1&$1\\\\\n /"          \
        $(  ps  -Csh -opid=  | 
            sed "s|$|/cmdline|")   /dev/null
' '--' ''\''' ''\''\\'\'''\''' 
arg 6:  'sh' 
''\''./sh'\'' '\''-IE'\'' '\
 ''\''sh'\'' '\
 ''\''./sh'\'' '\''-E'\'' '\
 ''\''../sh'\'' '\
 ''\''sh'\'' '\''-c'\'' '\''
        cd /proc; unset IFS
        sed  -sze "H;$"\!"d;x"                  \
             -e   "s/$1/$2\\\\$2$2/g"           \
             -e   "s/\x00\([^\x00]*\)/$2\1$2 /g"\
             -e   "s/.*/$1&$1\\\\\n /"          \
        $(  ps  -Csh -opid=  |
            sed "s|$|/cmdline|")   /dev/null
'\'' '\''--'\'' '\'''\''\'\'''\'''\'' '\'''\''\'\'''\''\\'\''\'\'''\'''\''\'\'''\'''\'' '\
sed: can't read 31725/cmdline: No such file or directory
 ''\''sh'\'' '

관련 정보