2009-06-14 68 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']