2009-06-14 139 views
8

大多数脚本解析的/ proc/CMDLINE它分解成词,然后筛选出的参数与case语句,例如:分裂的/ proc/CMDLINE参数用空格

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"。或者这是一个不好的解决方案?

+0

你的shell已经在解析命令行,拆分参数。你为什么要“解开”它,而不是假设用户会正确引用这个论点 - 就像它与任何其他命令一样?如果你想删除一个带有空格的文件,你可以用空格“»键入«rm»文件。如果你的ESSID是“essid wlan = FOO”呢? – liori 2009-06-14 20:44:56

+0

好吧,我做了与JesperE一样的错误... – liori 2009-06-14 21:33:15

回答

-4

从shell脚本使用/ proc/cmdline访问命令行是错误的方法。为什么不使用$ @?

for arg in "[email protected]"; do 
    ... 
done 

编辑:在$ @周围添加引号。

编辑:误读了这个问题;/proc/cmdline是内核命令行。

+0

/proc/cmdline是如何在Linux发行版上设置参数的。例如http://webconverger.org/boot/ – hendry 2009-06-14 19:32:06

+0

请记住在$ @周围使用引号,否则它将在for循环中拆分。用于“$ @”中的arg;做... – 2009-06-14 19:40:30

+0

@hendry:对不起,我误解为“/ proc/PID/cmdline”。/proc/cmdline是正在运行的内核的命令行,/ proc/PID/cmdline是过程PID的命令行。我仍然不明白你为什么要解析内核命令行。我对内核黑客知之甚少,但听起来像解析/ proc/cmdline来弄清楚运行内核的信息是错误的方法。 – JesperE 2009-06-14 21:28:23

0

在豪华:

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

您可以定义一个函数,并给您$ CMDLINE 未加引号作为参数传递给函数。然后你会调用shell的解析机制。请注意,你应该在它将要工作的shell上测试它--zsh用引用来做一些有趣的事情;-)。

然后,你可以告诉用户做壳报价一样:

#!/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 

(豪华 - 符合政策的普通的shell,剥夺超出标准的POSIX任何功能壳)

+0

因为你可以吃更多的参数(2美元,3美元......和更多的班次)。我同意这对x = y参数没有那么有用;更多用于-x y。但是这个解决方案更通用,更具惯用性(在shell脚本中经常看到它)。 – liori 2009-06-19 21:29:58

3

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

在Bash中,printf可以用来避开它们,例如,

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

有一些方法:

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

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

这些将大部分情况下工作,但是当争吵在他们的空间将失败。不过,我认为会有比使用/proc/PID/cmdline更好的方法。

1

你可以做类似下面的使用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 

那么你就引用和/或逃避的东西,就像你会在一个正常的外壳。

8
set -- $(cat /proc/cmdline) 
for x in "[email protected]"; do 
    case "$x" in 
     wlan=*) 
     echo "${x#wlan=}" 
     ;; 
    esac 
done 
-1

发现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 
1

既然你希望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']