追加読書

追加読書

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/cmdline次のようにnulを処理してみてください。^@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のようなシステムの場合は、システムの外部またはprintf組み込みprintfshこれは%qの人気より低いです。したがって、人々は他人のものを使用する必要があります。-0xargsprintf

i*/ の場合 /proc/[0-9]
する
    デフォルト名「$i」
    < $i/cmdline xargs -0 zsh -c 'printf "\t%q" "$0" "$@"'
    printf '\n'
完璧

プログラムの開始時に使用されるコマンド名とパラメーターは保証されません。プログラムはここに表示される内容を変更できます。多くの人がこれを行い、setproctitle()引数文字列を台無しにする誤った関数を使用していることに気付くでしょう。あなたができることは何もありません。 各プログラム自体無効なパラメータ文字列を設定して、すべてを1つのパラメータに入れました。

追加読書

答え4

私は次のすべてが標準のPC Linuxディストリビューションであると仮定していることから始めます。たとえば、テストするときに、デフォルトのArch Linuxインストールをある程度使用しました。


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

...最初に実行中のプロセスコマンドラインのリストを印刷し、正規表現に一致するものだけでリストをフィルタリングします。\[x]ss-lock。これ\[x]...正規表現は、次のような複数の名前を一覧表示するリストを処理するための非常に一般的な解決策として使用されます。xss-lock単語を含むコマンドを使用した他のリストのフィルタリングxss-lock

しかし、これが最善の方法ではありません。 Linuxシステムpsは通常procps-ng psこれは-C私が知っている限りAIXのps

とにかく、ゲームの一部を放棄することができます。たとえば、次のようになります。

ps -C xss-lock -ocommand

...コマンド名が実行されているプロセスのコマンドラインのみを印刷する場合(あなたはまたそれらをリストすることができますps -Aocomm=正確に一致xss-lock-grep -Fxフィルタと同じです。到着実際に正規表現によるフィルタリングpgrep同様の競合のない正規表現マッチングのためのツールもあります。


同じ名前、procps-ng ps解析ツリーのファイルを介して機能します/procps -ocommandたとえば、/proc/$pid/cmdline印刷する各項目のファイルを読み取って置き換えます。\0ゼロS(最後のものがなければEwlineになります\n~へ(ファイルの各パラメータを分離します)それぞれスペースがあります。

シェル引用引数のリストが必要な場合は、同様のことを行う必要があります。リストを引用する最も簡単な方法は、'ハード引用符を使用することです。ハード参照文字列は常に単純なので、最も単純です。ハード参照文字列に他のハード参照を含めることはできないため、解析中に深い再帰はありません。だから...

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

...単一のハード参照文字列ではなく、3つの連結文字列です。まず、it両側は二重引用符で囲まれています。 2番目は、アポストロフィで囲まれた単一のバックスペースです。単一の文字列ではありません。、最初の項目のようにハード参照されます。シェルは、引用符付きの3つの文字列をすべて1つのシェルに結合します。言葉解析するとき。

ファイルに対しても同じことができます/proc/$pid/cmdline。各ファイルを次のように分割する必要があります。\0ゼロバイト内の各アポストロフィを前に付けます。'\''を選択し、各オブジェクトをアポストロフィで囲み、\t各オブジェクト間に1つ以上のabまたは空白文字を挿入します。

今考えてみると、最初のバージョンは不必要に複雑で、実際にエラーが発生しやすくなりました。直感的に、私は各コ​​マンドの内容をハード引用しようとしました。議論- したがって、単一のオブジェクトのすべてのオブジェクトから/proc/$pid/file最初のオブジェクトを減算します。しかし、これはうまくいきません。(実際には、結果全体のコマンドラインの参照レベルを完全に反転させます。)コマンド名に奇数のアポストロフィが含まれている場合。とにかく関係ありません。コマンドラインから実行しても機能します'cat'cat実際には動作しますより良いものシェルエイリアスを解釈できると、最初のエイリアスをcat他の場所よりも見つける可能性が高いためです。/proc/$pid/file(何が起こったかのようにcat


だからmiehのperlスクリプトに似たものを作成しましたが、GNUツールを使用しました。特に、このsed -zオプションはGNUにsed入力を次に分割するように指示します。\0ゼロewlines の代わりに bytes \n-s各ファイル引数が別々の入力ストリームとして処理されることを示すオプションを含む$最後の行は各パラメータに対して別々に参照でき、H各パラメータに対して前のスペースは再初期化されます。)。コマンド名と一致する実行中のプロセスごとにps -Csh -opid=pid番号が1行に印刷さshれ、sed "s|$|/cmdline|"パス名が各プロセスに追加されます。

だから$IFSunset$(cmdsub's)出力はシェルによって空白の個々の引数に確実に分割されます(パス名ごとに\n1つの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'\'' '

関連情報