2012-10-08 15 views
5

Wszyscy mówią, że eval jest zły i powinieneś użyć $() jako zamiennika. Ale natknąłem się na sytuację, w której nieobsługiwany nie jest traktowany tak samo wewnątrz $().

Tło jest to, że byłem zbyt często nagrywany przez ścieżki do plików z miejscami w nich, i tak jak zacytować wszystkie takie ścieżki. Więcej paranoi na temat tego, skąd się biorą wszystkie moje pliki wykonywalne. Jeszcze bardziej paranoiczny, nie ufający sobie i tak jak możliwość wyświetlania utworzonych poleceń, które mam zamiar uruchomić.

Poniżej postaram wariacje na temat używania eval vs. $() i czy nazwa polecenia jest cytowany (bo to mógłby zawierać spacji)

BIN_LS="/bin/ls" 
    thefile="arf" 
    thecmd="\"${BIN_LS}\" -ld -- \"${thefile}\"" 

    echo -e "\n Running command '${thecmd}'" 
    $($thecmd) 

      Running command '"/bin/ls" -ld -- "arf"' 
     ./foo.sh: line 8: "/bin/ls": No such file or directory 

    echo -e "\n Eval'ing command '${thecmd}'" 
    eval $thecmd 

      Eval'ing command '"/bin/ls" -ld -- "arf"' 
     /bin/ls: cannot access arf: No such file or directory 

    thecmd="${BIN_LS} -ld -- \"${thefile}\"" 

    echo -e "\n Running command '${thecmd}'" 
    $($thecmd) 

      Running command '/bin/ls -ld -- "arf"' 
     /bin/ls: cannot access "arf": No such file or directory 

    echo -e "\n Eval'ing command '${thecmd}'" 
    eval $thecmd 

      Eval'ing command '/bin/ls -ld -- "arf"' 
     /bin/ls: cannot access arf: No such file or directory 

    $("/bin/ls" -ld -- "${thefile}") 

     /bin/ls: cannot access arf: No such file or directory 

Tak ... to jest mylące. Cytowana ścieżka polecenia jest poprawna wszędzie, z wyjątkiem konstrukcji $()? Krótsza, bardziej bezpośredni przykład:

$ c="\"/bin/ls\" arf" 
$ $($c) 
-bash: "/bin/ls": No such file or directory 
$ eval $c 
/bin/ls: cannot access arf: No such file or directory 
$ $("/bin/ls" arf) 
/bin/ls: cannot access arf: No such file or directory 
$ "/bin/ls" arf 
/bin/ls: cannot access arf: No such file or directory 

Jak można wytłumaczyć prostym $($c) sprawę?

Odpowiedz

3

Z tym, że to nie ma sensu. Use an array instead.

$ c=("/bin/ls" arf) 
$ "${c[@]}" 
/bin/ls: cannot access arf: No such file or directory 
+0

Nie wycięte łańcuchy są natychmiast oceniane. c = ("/ bin/ls"/bin/l *) robi coś zupełnie innego niż chciał. (spójrz na echo "'$ {c [@]}" ") c = ("/bin/ls ""/bin/l * "), a następnie z = $ (" $ {c [@]} ") zawiedzie. Tablica argumentów nie może być ładnie użyta z $()? – Shenme

+1

Jednak c = ("/ bin/ls" "/ bin/l *") wtedy z = $ ($ {c [@]}) wydaje się działać dobrze. Czy jest to wykonywalna forma $(), którą zamierzałeś mi pokazać? – Shenme

5

Zastosowanie " zacytować słowa jest częścią interakcji z bash. Po wpisaniu

$ "/bin/ls" arf 

w wierszu lub w skrypcie bash mówisz, że komenda składa się ze słów /bin/ls i arf i podwójnych cudzysłowach są naprawdę podkreślając, że /bin/ls jest jedno słowo.

Po wpisaniu

$ eval '"/bin/ls" arf' 

mówisz Bash że komenda składa się ze słów eval i "/bin/ls" arf. Ponieważ celem eval jest udawać, że jej argument jest rzeczywiste wejście polecenia człowieka, jest to równoznaczne z prowadzeniem

$ "/bin/ls" arf 

i " zostanie przetworzony tak jak w wierszu.

Należy pamiętać, że to pozory jest specyficzne dla eval; Bash zwykle nie robi tego, by udawać, że coś jest prawdziwym poleceniem pisanym przez człowieka.

Po wpisaniu

$ c='"/bin/ls" arf' 
$ $c 

$c zostaje podstawiona, a następnie ulega słowo łupania (patrz §3.5.7 "Word Splitting" in the Bash Reference Manual), więc słowa komendy są "/bin/ls" (uwaga na cudzysłów!) I arf. Nie trzeba dodawać, że to nie działa. (To także nie jest zbyt bezpieczne, ponieważ oprócz podziału na słowa, $c również podlega rozszerzeniu nazwy pliku i generalnie.Ogólnie, rozszerzenia parametrów powinny zawsze być w podwójnych cudzysłowach, a jeśli nie mogą, to należy przepisać Kod może być. Niecytowane rozszerzenia parametrów proszą o kłopoty.)

Po wpisaniu

$ c='"/bin/ls" arf' 
$ $($c) 

ten jest taki sam jak poprzednio, tyle że teraz jesteś również stara się wykorzystać wyjście polecenia niepracującą jako nowe polecenie . Nie trzeba dodawać, że nie powoduje to, że polecenie nonworking nagle zadziała.

Jak Ignacio Vazquez-Abrams mówi w swej odpowiedzi, dobrym rozwiązaniem jest użycie tablicy i obsługiwać cytowanie prawidłowo:

$ c=("/bin/ls" arf) 
$ "${c[@]}" 

który ustawia c do tablicy z dwóch elementów, /bin/ls i arf, i używa tych dwóch elementów jako słowa polecenia.

+0

(Nienawidzę limitu 5 min) Ten ostatni wiersz nie pokazuje wykonania polecenia pakowanego w formularzu, w którym można przechwycić dane wyjściowe (które jednak nie zostały podane w pytaniu oryginalnym) Spróbuj c = ("/ bin/ls" "/ bin/l * ") i c = ("/bin/ls "/ bin/l *) z echo" '$ {c [@]} "". Ta metoda nie pozwoli mi zbudować tablicy z cytowanymi argumentami do późniejszego użycia z $() ...? – Shenme

+1

@Shenme: O ile wiem, mówisz: "Chcę użyć' eval', ponieważ chcę przetworzyć dowolny ciąg jako polecenie Bash. " Wszyscy mówią, że "eval" jest złe, ponieważ przetwarza dowolny ciąg jako polecenie Bash."Próbujesz pogodzić te punkty widzenia, pytając:" Czy istnieje jakiś * nie * -ilowy sposób na zrobienie tego złego? ", Ale to nie zadziała, a punkty widzenia są zasadniczo nie do pogodzenia. narzędzie zła lub, aby uniknąć złego narzędzia, przepisaj swój scenariusz, aby uniknąć czynienia złego. – ruakh

+0

Nie, nie ma żadnych arbitralnych poleceń, żadnych złych intencji. :-) Po prostu chciałem zbudować polecenie i wykonać je w kontrolowany sposób Będę kontrolował dane wejściowe, a nawet jestem wystarczająco paranoiczny, by użyć "-". Ludzie widzą uproszczone odpowiedzi na skomplikowane meta-pytania i krytykują cię, jeśli "wykorzystujesz" eval, więc chciałem uniknąć krytyki. - (W każdym razie, z = $ ($ {c [@]}) działa bez zarzutu – Shenme

2

Z man page for bash, dotyczące eval:

eval [arg ...]: args są czytane i łączone ze sobą w jednym poleceniu. To polecenie jest następnie odczytywane i wykonywane przez powłokę, a jego status wyjścia jest zwracany jako wartość eval.

Kiedy c jest zdefiniowany jako "\"/bin/ls\" arf", zewnętrzne cytaty spowoduje cała sprawa być przetwarzane jako pierwszy argument eval, która ma być polecenia lub programu. Musisz przekazać argumenty eval w taki sposób, aby polecenie docelowe i jego argumenty były wymienione osobno.

Konstrukcja zachowuje się inaczej niż eval, ponieważ nie jest to polecenie, które pobiera argumenty. Może przetwarzać całą komendę naraz zamiast przetwarzać argumenty pojedynczo.

Notatka na temat pierwotnego założenia: Głównym powodem, dla którego ludzie mówią, że eval jest zły, jest to, że jest on często używany przez skrypty do wykonywania łańcucha podanego przez użytkownika jako polecenia powłoki. Chociaż czasami jest to przydatne, jest to poważny problem bezpieczeństwa (zazwyczaj nie ma praktycznego sposobu na sprawdzenie łańcucha przed jego wykonaniem). Problem bezpieczeństwa nie ma zastosowania, jeśli używasz eval na sztywno napisanych ciągach w twoim skrypcie, tak jak to robisz. Jednak zazwyczaj łatwiejsze i bardziej użyteczne jest używanie wewnątrz skryptów do podstawiania poleceń, pozostawiając rzeczywisty przypadek pozostały dla eval.

+0

Dodatkowa wskazówka: podczas próby sprawdzenia, jak rozwinięte polecenie jest widziane przez powłokę, użycie 'set -v' może czasami dać dokładniejsze wyniki niż' echo'. – bta

+0

, więc w zasadzie "eval" jest bezpieczne, jeśli oszacowany ciąg nie jest w żaden sposób narażony na dane wejściowe użytkownika; ale jeśli nie uda nam się temu zapobiec, można go wykorzystać; dlatego użycie '' ... '' lub '$ (...)' wymaga mniejszej ostrożności w porównaniu do 'eval'; thx man! to była konkretna porada, której szukałem przed zmianą moich skryptów hehe! :) –

Powiązane problemy