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 这样的系统,其中系统外部的printf
和printf
内置的sh
都没有。这比的%q
普及程度要低。因此,人们不得不求助于使用别人的。-0
xargs
printf
对于 /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
通过解析树中的文件进行操作/proc
。ps -ocommand
例如,对于您的,它会读取/proc/$pid/cmdline
应打印的每个匹配项的文件并替换\0
零点s(少了最后一个 - 它变成了一条\n
ewline )其中(它分隔文件中的每个参数)每个都有一个空格。
如果你想要 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 而不是\n
ewlines,其-s
选项指示将每个文件参数视为单独的输入流(因此$
最后一行可能会为每个参数单独引用,并且H
旧空间会为每个参数重新初始化)。对于每个与命令 name 匹配的正在运行的进程,每行打印ps -Csh -opid=
一个 pid 号sh
,然后sed "s|$|/cmdline|"
将该路径名附加到每个进程。
因为$IFS
是unset
,$(cmdsub's)
输出肯定会被 shell 分割成空格上的单独参数 -\n
每个路径名只有一个 ewline - 并且进程会获取在查找它们时存在的sed -sze
所有路径名的参数列表。最后一点重新介绍了整个赛车游戏,当尝试读取时打印的错误消息表明了这一点/proc/$pid/cmdline
ps -C
sed -sze
cmdline
命令子进程的文件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 -c
pid:
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'\'' '