代码之家  ›  专栏  ›  技术社区  ›  hendry

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

  •  9
  • hendry  · 技术社区  · 15 年前

    大多数解析/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" .或者这是一个糟糕的解决方案?

    8 回复  |  直到 15 年前
        1
  •  34
  •   Kevin    12 年前

    有一些方法:

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

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

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

        2
  •  11
  •   Natanael Copa    11 年前
    set -- $(cat /proc/cmdline)
    for x in "$@"; do
        case "$x" in
            wlan=*)
            echo "${x#wlan=}"
            ;;
        esac
    done
    
        3
  •  4
  •   ephemient    15 年前

    最常见的是, \0ctal 当空间不可接受时,使用转义序列。

    在Bash, printf 可用于取消它们的景观,例如。

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

    由于希望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的语法。 在这种情况下,您甚至可能不再需要外壳。 例如,对于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']
    
        5
  •  3
  •   RainDoctor    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
    
        6
  •  2
  •   codemonkeyjohn    13 年前

    可以使用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
    

    然后你会引用和/或逃避一些东西,就像在普通的外壳中一样。

        7
  •  1
  •   liori    15 年前

    时髦的:

    $ 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之外的任何功能)

        8
  •  -1
  •   Community Egal    7 年前

    建立 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