shell 使用空格拆分/proc/cmdline参数

mfpqipee  于 2022-12-19  发布在  Shell
关注(0)|答案(8)|浏览(345)

大多数解析/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循环在空格上分裂。
有没有更好的方法来从shell脚本中解析/proc/cmdline,而几乎没有对它求值?
或者有一些引用技巧吗?我在想,我也许可以让用户实体引用空格,并像这样解码:/bin/busybox httpd -d "FOO%20BAR"。或者这是一个糟糕的解决方案吗?

hwamh0ep

hwamh0ep1#

有几种方法:
1.第一个月

  1. cat /proc/PID/cmdline | xargs -0 echo
    这在大多数情况下都可以使用,但是当参数中有空格时会失败,但是我确实认为会有比使用/proc/PID/cmdline更好的方法。
lsmepo6l

lsmepo6l2#

set -- $(cat /proc/cmdline)
for x in "$@"; do
    case "$x" in
        wlan=*)
        echo "${x#wlan=}"
        ;;
    esac
done
zzoitvuj

zzoitvuj3#

最常见的是,当空格不可接受时使用\0ctal转义序列。
在Bash中,printf可以用来取消转义它们,例如:

CMDLINE='quiet union=aufs wlan=FOO\040BAR'
for x in $CMDLINE; do
    [[ $x = wlan=* ]] || continue
   printf '%b\n' "${x#wlan=}"
done
zmeyuzjn

zmeyuzjn4#

由于您希望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']
zlwx9yxi

zlwx9yxi5#

使用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
pvabu6sv

pvabu6sv6#

您可以使用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中一样。

rsl1atfo

rsl1atfo7#

豪华:

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

你可以定义一个函数,并给予$CMDLINE unquoted 作为函数的参数,然后调用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 ,该 shell 去除了标准POSIX以外的任何功能)

lnlaulya

lnlaulya8#

我发现here是使用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

相关问题