2009-06-14 14 views
8

Większość skryptów, które parse/proc/cmdline złamać go w słowa, a następnie odfiltrować argumenty z oświadczeniem Case, przykład:Dzielenie/proc/cmdline ze spacjami

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

Problem jest, gdy WLAN ESSID ma spacje. Użytkownicy oczekują ustawienia wlan='FOO BAR '(jak zmienna powłoki), a następnie uzyskają nieoczekiwany wynik 'FOO z powyższym kodem, ponieważ pętla for podzieli się na spacje.

Czy istnieje lepszy sposób analizowania /proc/cmdline ze skryptu powłoki, który jest prawie nieoceniony?

A może masz jakieś sztuczki? Pomyślałem, że mógłbym poprosić użytkowników o utworzenie cudzysłowów i dekodowanie w ten sposób: /bin/busybox httpd -d "FOO%20BAR". A może to złe rozwiązanie?

+0

Twoja powłoka już analizuje wiersz poleceń, dzieląc argumenty. Dlaczego chcesz go "odseparować", a nie tylko założyć, że użytkownik prawidłowo przytoczy argument - tak jak ma to miejsce w przypadku każdego innego polecenia? Jeśli chcesz usunąć plik z spacją, wpisz "rm" z spacją "». Co jeśli twój ESSID to "essid wlan = FOO"? – liori

+0

Ok, zrobiłem ten sam błąd co JesperE ... – liori

Odpowiedz

-4

Użycie/proc/cmdline z skryptu powłoki w celu uzyskania dostępu do wiersza poleceń jest niepoprawne. Dlaczego nie skorzystać z @ @?

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

EDYCJA: dodano kwotowania około $ @.

EDYCJA: błędne odczytanie pytania;/proc/cmdline to linia komend jądra.

+0

/proc/cmdline to sposób ustawiania argumentów w dystrybucjach Linuksa. na przykład http://webconverger.org/boot/ – hendry

+0

Pamiętaj, aby użyć cudzysłowów wokół $ @ lub zostanie podzielony na pętlę for. Użyj dla arg w "$ @"; do ... –

+0

@hendry: przepraszam, błędnie odczytałem to jako "/ proc/PID/cmdline"./proc/cmdline to linia poleceń uruchomionego jądra, a/proc/PID/cmdline to linia poleceń procesu PID. Nadal nie rozumiem, dlaczego chcesz parsować linię poleceń jądra. Nie wiem zbyt wiele o hakowaniu jądra, ale brzmi to jak parsowanie/proc/cmdline, aby dowiedzieć się, że informacje o działającym jądrze są niewłaściwe. – JesperE

0

W posh:

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

Można zdefiniować funkcję i podać $ CmdLine cytowane jako argument funkcji. Następnie wywołasz mechanizmy parsowania powłoki. Zauważ, że powinieneś przetestować to na powłoce, w której będzie pracował - zsh robi pewne śmieszne rzeczy z cytowaniem ;-).

Następnie można po prostu powiedzieć użytkownikowi zrobić powołując jak w skorupkach:

#!/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 - zgodne z zasadami zwyczajnego powłoki, powłoki uproszczoną żadnej funkcji poza standardem POSIX)

+0

Ponieważ możesz jeść więcej argumentów (z 2 $, 3 $ ... i więcej zmian). Zgadzam się, że nie jest to zbyt użyteczne dla argumentów x = y; więcej dla -x y. Ale to rozwiązanie jest bardziej uniwersalne i nieco bardziej idiomatyczne (często spotykane w skryptach powłoki). – liori

+0

W jaki sposób użytkownicy będą podawać esej jako "pasek foo"? wlan = foo \ 040bar jak druga odpowiedź? Szczerze myślę, że kodowanie adresu URL "% 20" jest łatwiejsze dla przeciętnego użytkownika niż kodowanie ósemkowe powłoki. – hendry

+1

W takim przypadku zastosowanie będzie wyglądać jak w powłoce: CMDLINE = "quiet wlan =" foo bar bar union = of = consumer and-yet-another word "union = aufs". Ale ... teraz myślę, że jeśli ma to być również parametry jądra, powinieneś raczej używać ósemkowego. Kernel nie robi ekspansji powłoki i może być mylony, widząc powyższy identyfikator wlan. – liori

3

najczęściej , \0ctal Sekwencje specjalne są używane, gdy spacje są niedopuszczalne.

W Bashu można użyć do ich usunięcia, np.

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

Preferuję cytowanie obiektów w stylu Web. – hendry

+0

Oktaficzne ucieczki są bardziej powszechne (i tradycyjne) w środowiskach UNIX. Oto jak na przykład dodać spacje do montowania ścieżek w/etc/fstab. – ephemient

+0

Ponieważ ten parametr dla "produktu Web" Webconverger, gdzie inne parametry, takie jak strona główna http://webconverger.org/boot/, będą również kodowane za pomocą adresu URL, uważam, że mój początkowy wybór jest najlepszy. – hendry

10

Istnieje kilka sposobów:

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

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

Będą pracować z większości przypadków, ale nie powiedzie się, gdy argumenty mają spacje w nich. Sądzę jednak, że byłoby lepsze podejście niż użycie /proc/PID/cmdline.

+4

'/ proc/cmdline' jest zupełnie inny od'/proc/PID/cmdline' ... –

+0

Bardziej konkretny '/ proc/cmdline' jest wierszem poleceń dostarczanym do jądra przez niektóre bootloadery (GrUB, LiLo, SysLinux itp.). '/ proc/$ PID/cmdline' to linia poleceń przetwarzana przez wywołanie systemowe' execve() 'dla każdego procesu. –

+0

to lepsze rozwiązanie niż najbardziej upominane: krótkie i na temat (zastąp "\ 000" spacjami za pomocą standardowego narzędzia) – akappa

1

Można zrobić coś jak następujące używając bash, które uczyniłoby te argumenty do zmiennych takich jak $ cmdline_union i $ cmdline_wlan:

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

Wtedy cytujesz i/lub ucieczki rzeczy tak jak byś w normalna skorupa.

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

bardzo eleganckie rozwiązanie! –

+0

'dla x; do '... domyślnie do tej samej semantyki jak' dla x w "$ @"; do ... ' Ale bardziej wyraźna forma jest ... bardziej jednoznaczna. Tylko notatkę boczną. :) –

-1

Znaleziony here dobry sposób, aby zrobić to z awk, niestety to będzie działać tylko z 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

Ponieważ chcesz powłoka do analizowania/proc/cmdline treści, trudno unikaj tego.

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

To jest oczywiście niebezpieczne choć jak to ślepo uruchomić cokolwiek, który został podany w linii poleceń jądra jak quiet union=aufs wlan=FOO) ; touch EVIL ; q=(q.

Uciekające spacje (\ x20) brzmią jak najprostsza i bezpieczna droga.

Ciężką alternatywą jest użycie parsera, który rozumie składnię podobną do powłoki. W takim przypadku możesz już nie potrzebować powłoki. Na przykład z pytonem:

$ 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']