2009-06-14 11 views
8

Die meisten Skripte, die Parse/proc/cmdline es brechen in Worte zu fassen und dann Argumente mit einer case-Anweisung herauszufiltern, zB:Splitting/proc/cmdline Argumente mit Leerzeichen

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

Das Problem ist, wenn die WLAN-ESSID hat Räume. Benutzer erwarten wlan='FOO BAR '(wie eine Shell-Variable) und erhalten dann das unerwartete Ergebnis 'FOO mit dem obigen Code, da die FOR-Schleife auf Leerzeichen aufgeteilt.

Gibt es eine bessere Möglichkeit, die /proc/cmdline von einem Shell-Skript zu analysieren, das es fast ausschließt?

Oder gibt es ein paar Tricks? Ich dachte, ich könnte vielleicht Benutzer bitten, Leerzeichen zu entziffern und so zu dekodieren: /bin/busybox httpd -d "FOO%20BAR". Oder ist das eine schlechte Lösung?

+0

Ihre Shell analysiert bereits die Befehlszeile, Argumente zu teilen. Warum wollen Sie es "aufsparen" und nicht einfach davon ausgehen, dass der Benutzer das Argument korrekt zitiert - wie es mit jedem anderen Befehl zu tun hat? Wenn Sie eine Datei mit Leerzeichen entfernen möchten, geben Sie «rm» mit Leerzeichen "» ein. Was ist, wenn Ihre ESSID «essid wlan = FOO» ist? – liori

+0

Ok, ich habe den gleichen Fehler gemacht wie JesperE ... – liori

Antwort

-4

Die Verwendung von/proc/cmdline aus einem Shell-Skript für den Zugriff auf die Befehlszeile ist der falsche Weg. Warum nicht $ @ benutzen?

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

BEARBEITEN: Zitate um $ @ hinzugefügt.

EDIT: falsch gelesen die Frage;/proc/cmdline ist die Kernel Befehlszeile.

+0

/proc/cmdline ist, wie Argumente auf Linux-Distributionen gesetzt werden. z.B. http://webconverger.org/boot/ – hendry

+0

Denken Sie daran, Zitate um $ @ zu verwenden oder es wird in der for-Schleife aufgeteilt. Verwenden Sie für arg in "$ @"; do ... –

+0

@hendry: Entschuldigung, ich habe es falsch gelesen als "/ proc/PID/cmdline"./proc/cmdline ist die Befehlszeile des ausgeführten Kernels und/proc/PID/cmdline ist die Befehlszeile der Prozess-PID. Ich verstehe immer noch nicht, warum Sie die Kernel-Kommandozeile analysieren wollen. Ich weiß nicht viel über Kernel-Hacking, aber es klingt wie parsing/proc/cmdline, um Informationen über den laufenden Kernel herauszufinden, ist der falsche Ansatz. – JesperE

0

In posh:

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

Sie können eine Funktion definieren, und geben Sie Ihren $ CMDLINE unquoted als Argument an die Funktion. Dann rufen Sie die Parsing-Mechanismen der Shell auf. Beachten Sie, dass Sie dies auf der Shell testen sollten, in der es arbeiten wird - zsh macht einige lustige Dinge mit Zitaten ;-).

Dann können Sie einfach dem Benutzer sagen, wie in der Schale zu tun zu zitieren:

#!/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 - richtlinienkonforme normal Windows Explorer, eine Hülle von Funktionen über Standard POSIX gestrippt)

+0

Weil Sie mehr Argumente essen können (mit $ 2, $ 3 ... und mehr Schichten). Ich stimme zu, dass dies für x = y-Argumente nicht so nützlich ist; mehr für -x y. Aber diese Lösung ist vielseitiger und etwas idiomatischer (sehr oft in Shell-Skripten). – liori

+0

Also, wie würden Benutzer einen Essid wie 'foo bar' zitieren? wlan = foo \ 040bar wie die andere Antwort? Ich glaube wirklich, URL-Codierung "% 20" ist einfacher für den durchschnittlichen Benutzer als Shell-Oktal-Codierungen. – hendry

+1

In diesem Fall wird ein Shell-ähnliches Escaping angewendet: CMDLINE = "quiet wlan = 'foo bar bar union = of = Verbraucher und-noch-ein anderes Wort' union = aufs". Aber ... jetzt denke ich, dass, wenn dies auch Kernel-Parameter sein sollen, du lieber Oktal verwenden solltest. Der Kernel führt keine Shell-Erweiterung durch und kann verwirrt sein, wenn er oberhalb der Wlan-ID angezeigt wird. – liori

3

Am häufigsten \0ctal Escape-Sequenzen werden verwendet, wenn Leerzeichen nicht akzeptabel sind.

In Bash printf kann verwendet werden, um sie, z.

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

Ich bevorzuge Web-Stil-Entity-Zitat. – hendry

+0

Octal-Escapes sind häufiger (und traditionell) in UNIX-Umgebungen. So fügen Sie beispielsweise Leerzeichen hinzu, um Pfade in/etc/fstab einzuhängen. – ephemient

+0

Da dies ein Parameter für ein "Web-Produkt" Webconverger, wo andere Parameter wie Homepage http://webconverger.org/boot/ auch URL-codiert werden, denke ich, meine erste Wahl ist am besten. – hendry

10

Es gibt einige Möglichkeiten:

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

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

Diese werden mit den meisten Fällen funktionieren, werden aber nicht als Argumente Räume in ihnen. Ich denke jedoch, dass es bessere Ansätze als die Verwendung von /proc/PID/cmdline geben würde.

+4

'/ proc/cmdline' ist ziemlich verschieden von'/proc/PID/cmdline' ... –

+0

Um genauer zu sein '/ proc/cmdline' ist die Kommandozeile, die ein Bootloader (GrUB, LiLo, SysLinux) dem Kernel zur Verfügung stellt , etc). '/ proc/$ PID/cmdline' ist die Befehlszeile, die von einem' execve() 'Systemaufruf für jeden Prozess verarbeitet wird. –

+0

das ist eine bessere Lösung als die am meisten Upvoted: kurz und auf den Punkt (ersetzen Sie \ 000 mit Leerzeichen mit einem Standardwerkzeug) – akappa

1

Man könnte so etwas wie die folgenden mit bash tun, die diese Argumente in den Variablen wie $ cmdline_union drehen würde und $ cmdline_wlan:

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

Dann würden Sie zitieren und/oder Dinge zu entkommen wie würden Sie in eine normale Hülle.

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

sehr elegante Lösung! –

+0

'für x; do '... verwendet standardmäßig dieselbe Semantik wie 'for x in $ @'; do ... ' Aber die explizite Form ist ... expliziter. Nur eine Randnotiz. :) –

-1

gefunden here eine schöne Möglichkeit, es mit awk zu tun, leider wird es funktionieren nur mit doublequotes:

# 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

Da Sie die Shell das/proc/cmdline Inhalte analysieren möchten, ist es schwer zu vermeide es, es auszuwerten.

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

Das ist natürlich gefährlich, obwohl, wie es blindlings alles laufen würde, die auf der Kernel-Befehlszeile wie quiet union=aufs wlan=FOO) ; touch EVIL ; q=(q angegeben wurden.

Entweichende Leerzeichen (\ x20) klingt wie der einfachste und sicherste Weg.

Eine schwere Alternative ist die Verwendung eines Parsers, der Shell-ähnliche Syntax versteht. In diesem Fall brauchen Sie die Shell möglicherweise nicht einmal mehr. Zum Beispiel mit 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'] 
Verwandte Themen