进一步阅读

进一步阅读

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具有误导性 - 如果您尝试复制并粘贴它,该命令将不会执行与原始命令相同的操作。所以有没有一种类似于 Bash 的方法printf %q来打印正在运行的命令列表正确转义或引用参数

答案1

/proc/$pid/cmdline在 Linux 上,您可以从给定进程 ID 的命令中获取稍微更原始的参数列表。 args 由 nul 字符分隔。尝试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/cmdline正如 meuh 所指出的,您可以通过以 NUL 字节分隔的参数来获取 Linux 和 NetBSD 上的字符串。这是一种将它们转换为可运行命令行的快速而肮脏的方法。

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

输出看起来像:

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

您可以直接将其复制粘贴到 shell 中来运行它。

较短的变体(需要 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使用通用但非标准-0可以愉快地将- 终止的字符串转换为命令参数,并且(如问题所述)printf使用通用但非标准%q格式说明符可以愉快地将命令参数转换为 shell-带引号的字符串。

对于 /proc/[0-9] 中的 i*/
    基本名称“$i”
    < $i/cmdline xargs -0 printf '\t%q'
    printf '\n'
完毕

唯一棘手的部分是像 FreeBSD 这样的系统,其中系统外部的printfprintf内置的sh都没有。这比的%q普及程度要低。因此,人们不得不求助于使用别人的。-0xargsprintf

对于 /proc/[0-9] 中的 i*/
    基本名称“$i”
    < $i/cmdline xargs -0 zsh -c 'printf "\t%q" "$0" "$@"'
    printf '\n'
完毕

请注意,不能保证这些是程序启动时使用的命令名称和参数。程序可以改变这里看到的内容。您会发现他们中的很多人都这样做,并且他们使用了一个较差的setproctitle()函数来破坏参数字符串。您对此无能为力。 各个程序本身构造了一个错误的参数字符串,将所有内容放入一个参数中。

进一步阅读

答案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(少了最后一个 - 它变成了一条\newline )其中(它分隔文件中的每个参数)每个都有一个空格。

如果你想要 shell 引用的参数列表,你必须做类似的事情。对任何列表进行 shell 引用的最简单方法是使用'硬引号。它是最简单的,因为硬引用字符串总是平坦的 - 解析中没有深度递归,因为硬引用字符串不能包含另一个硬引用。所以...

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

...不是单个硬引用字符串,而是三个连接的字符串。首先,it两边都被硬引号包围;第二个是单个退格引用的撇号;第三个,不是单个字符串,与第一个一样被硬引用。 shell 将所有三个带引号的字符串组合到一个 shell 中单词解析时。

我们可以对文件做同样的事情/proc/$pid/cmdline。我们需要将每个文件拆分为\0零点字节,预先引用其中找到的每个撇号,例如'\'',然后用撇号包围每个对象,并\t在每个对象之间插入一个或多个 ab 或空格字符。

现在我正在考虑这个问题,我的第一个版本既不必要地复杂,而且实际上很容易出错。直觉上我试图硬引用每个命令的论点- 所以单个对象的所有对象都/proc/$pid/file减去第一个 - 但这不起作用(事实上​​,完全反转了整个结果命令行的引用级别)如果命令名称包含奇数个撇号。无论如何,这并不重要 -与在命令行运行时'cat'一样有效。cat事实上,它有效更好的因为如果可以解释 shell 别名,则第一个更有可能cat在 中找到,而不是其他情况/proc/$pid/file(就像发生的那样cat


所以我做了一个类似于 meuh 的 perl 脚本的事情,但是用的是 GNU 工具。具体来说,该sed -z选项指示 GNUsed将输入拆分为\0零点bytes 而不是\newlines,其-s选项指示将每个文件参数视为单独的输入流(因此$最后一行可能会为每个参数单独引用,并且H旧空间会为每个参数重新初始化)。对于每个与命令 name 匹配的正在运行的进程,每行打印ps -Csh -opid=一个 pid 号sh,然后sed "s|$|/cmdline|"将该路径名附加到每个进程。

因为$IFSunset,$(cmdsub's)输出肯定会被 shell 分割成空格上的单独参数 -\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它替换自身,而不是等待检查下一行输入。在这种情况下,sed -sze将设法读取自己的/proc/$pid/cmdline文件 - 因为它保留了sh -cpid:

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'\'' '

相关内容