16

大多数解析 /proc/cmdline 的脚本将其分解为单词,然后使用 case 语句过滤掉参数,例如:

CMDLINE="quiet union=aufs wlan=FOO"
for x in $CMDLINE
do
»···case $x in
»···»···wlan=*)
»···»···echo "${x//wlan=}"
»···»···;;
»···esac
done

问题是当 WLAN ESSID 有空格时。用户期望设置wlan='FOO BAR'(如 shell 变量)然后'FOO使用上述代码得到意想不到的结果,因为 for 循环在空格上拆分。

有没有更好的方法/proc/cmdline从几乎无法评估它的 shell 脚本中解析它?

还是有一些引用技巧?我在想我也许可以要求用户实体引用空格并像这样解码:/bin/busybox httpd -d "FOO%20BAR". 或者这是一个糟糕的解决方案?

4

8 回答 8

34

有一些方法:

  1. cat /proc/PID/cmdline | tr '\000' ' '

  2. cat /proc/PID/cmdline | xargs -0 echo

这些将适用于大多数情况,但当参数中有空格时会失败。但是我确实认为会有比使用更好的方法/proc/PID/cmdline

于 2011-05-26T03:01:26.267 回答
11
set -- $(cat /proc/cmdline)
for x in "$@"; do
    case "$x" in
        wlan=*)
        echo "${x#wlan=}"
        ;;
    esac
done
于 2013-02-22T15:47:42.363 回答
4

Most commonly, \0ctal escape sequences are used when spaces are unacceptable.

In Bash, printf can be used to unescape them, e.g.

CMDLINE='quiet union=aufs wlan=FOO\040BAR'
for x in $CMDLINE; do
    [[ $x = wlan=* ]] || continue
   printf '%b\n' "${x#wlan=}"
done
于 2009-06-17T17:47:30.540 回答
4

由于您希望 shell 解析 /proc/cmdline 内容,因此很难避免对其进行评估。

#!/bin/bash
eval "kernel_args=( $(cat /proc/cmdline) )"
for arg in "${kernel_args[@]}" ; do
    case "${arg}" in
        wlan=*)
            echo "${arg#wlan=}"
            ;;
    esac
done

这显然很危险,因为它会盲目地运行内核命令行上指定的任何内容,例如quiet union=aufs wlan=FOO ) ; touch EVIL ; q=( q.

转义空格 (\x20) 听起来是最直接和最安全的方法。

一个重要的替代方法是使用一些解析器,它可以理解类似 shell 的语法。在这种情况下,您甚至可能不再需要 shell。例如,使用 python:

$ cat /proc/cmdline
quiet union=aufs wlan='FOO BAR' key="val with space" ) ; touch EVIL ; q=( q
$ python -c 'import shlex; print shlex.split(None)' < /proc/cmdline
['quiet', 'union=aufs', 'wlan=FOO BAR', 'key=val with space', ')', ';', 'touch', 'EVIL', ';', 'q=(', 'q']
于 2017-03-16T21:21:54.580 回答
3

使用xargs -n1

[centos@centos7 ~]$ CMDLINE="quiet union=aufs wlan='FOO BAR'"
[centos@centos7 ~]$ echo $CMDLINE
quiet union=aufs wlan='FOO BAR'
[centos@centos7 ~]$ echo $CMDLINE | xargs -n1
quiet
union=aufs
wlan=FOO BAR

[centos@centos7 ~]$ xargs -n1 -a /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-3.10.0-862.14.4.el7.x86_64
root=UUID=3260cdba-e07e-408f-93b3-c4e9ff55ab10
ro
consoleblank=0
crashkernel=auto
rhgb
quiet
LANG=en_US.UTF-8
于 2020-09-27T20:09:23.477 回答
2

您可以使用 bash 执行以下操作,这会将这些参数转换为 $cmdline_union 和 $cmdline_wlan 等变量:

bash -c "for i in $(cat /proc/cmdline); do printf \"cmdline_%q\n\" \"\$i\"; done" | grep = > /tmp/cmdline.sh
. /tmp/cmdline.sh

然后你会像在普通 shell 中一样引用和/或转义一些东西。

于 2011-06-02T02:59:37.970 回答
1

豪华:

$ f() { echo $1 - $3 - $2 - $4 
> }
$ a="quiet union=aufs wlan=FOO"
$ f $a
quiet - wlan=FOO - union=aufs -

您可以定义一个函数并将您的 $CMDLINE 不带引号作为该函数的参数。然后您将调用 shell 的解析机制。请注意,您应该在将要运行的 shell 上对此进行测试——zsh 通过引用 ;-) 做了一些有趣的事情。

然后你可以告诉用户在 shell 中做引用:

#!/bin/posh
CMDLINE="quiet union=aufs wlan=FOO"
f() {
        while test x"$1" != x 
        do      
                case $1 in
                        union=*)        echo ${1##union=}; shift;;
                        *)              shift;; 
                esac    
        done    
}       
f $CMDLINE

(posh - 符合策略的普通 SHell,剥离了标准 POSIX 之外的任何功能的外壳)

于 2009-06-17T11:00:44.110 回答
-1

在这里找到了一种使用 awk 的好方法,不幸的是它只适用于双引号:

# Replace spaces outside double quotes with newlines
args=`cat /proc/cmdline | tr -d '\n' | awk 'BEGIN {RS="\"";ORS="\"" }{if (NR%2==1){gsub(/ /,"\n",$0);print $0} else {print $0}}'`

IFS='                                                                           
'                                                                               
for line in $args; do                                                           
     key=${line%%=*}                                                            
     value=${line#*=}                                                           

     value=`echo $value | sed -e 's/^"//' -e 's/"$//'`                          

     printf "%20s = %s\n" "$key" "$value"                                       

done
于 2013-04-26T09:21:23.443 回答