Chciałbym zapisać polecenie do użycia w późniejszym okresie w zmiennej (nie wynik polecenia, ale samo polecenie)
Mam następujący prosty skrypt:
command="ls";
echo "Command: $command"; #Output is: Command: ls
b=`$command`;
echo $b; #Output is: public_html REV test... (command worked successfully)
Jednak kiedy próbuję czegoś bardziej skomplikowanego, kończy się to niepowodzeniem. Na przykład, jeśli zrobię
command="ls | grep -c '^'";
Wynik to:
Command: ls | grep -c '^'
ls: cannot access |: No such file or directory
ls: cannot access grep: No such file or directory
ls: cannot access '^': No such file or directory
Masz jakiś pomysł, jak mógłbym przechowywać takie polecenie (z potokami / wieloma poleceniami) w zmiennej do późniejszego wykorzystania?
Odpowiedzi:
Użyj eval:
źródło
backticks
. y = $ (eval $ x) mywiki.wooledge.org/BashFAQ/082eval
jest dopuszczalną praktyką tylko wtedy, gdy ufasz zawartości swoich zmiennych. Jeśli używasz, powiedzmyx="ls $name | wc"
(a nawetx="ls '$name' | wc"
), ten kod jest szybką ścieżką do luk w zabezpieczeniach wstrzykiwania lub eskalacji uprawnień, jeśli ta zmienna może być ustawiona przez osobę o mniejszych uprawnieniach. (Na przykład przeprowadzanie iteracji po wszystkich podkatalogach w programie/tmp
? Lepiej zaufaj każdemu użytkownikowi w systemie, że nie wywoła żadnego$'/tmp/evil-$(rm -rf $HOME)\'$(rm -rf $HOME)\'/'
).eval
jest ogromnym magnesem na błędy, którego nigdy nie powinno się polecać bez ostrzeżenia o ryzyku nieoczekiwanego zachowania podczas analizowania (nawet bez złośliwych ciągów, jak w przykładzie @ CharlesDuffy). Na przykład spróbujx='echo $(( 6 * 7 ))'
i wtedyeval $x
. Można by się spodziewać, że wypisze "42", ale prawdopodobnie nie. Czy możesz wyjaśnić, dlaczego to nie działa? Czy możesz wyjaśnić, dlaczego powiedziałem „prawdopodobnie”? Jeśli odpowiedzi na te pytania nie są dla Ciebie oczywiste, nigdy nie dotykajeval
.set -x
wcześniej uruchomić, aby zarejestrować uruchomione polecenia, co ułatwi zobaczenie, co się dzieje.Czy nie używać
eval
! Istnieje duże ryzyko wprowadzenia dowolnego kodu.BashFAQ-50 - próbuję umieścić polecenie w zmiennej, ale złożone przypadki zawsze zawodzą.
Umieścić go w tablicy i rozwinąć wszystkie słowa z cudzysłowami
"${arr[@]}"
aby nie pozwólIFS
rozdzielić słowa ze względu na podział na słowa .i zobacz zawartość tablicy w środku.
declare -p
Pozwala zobaczyć zawartość wewnątrz tablicy z każdego parametru poleceń w osobnych indeksów. Jeśli jeden z takich argumentów zawiera spacje, cytowanie wewnątrz podczas dodawania do tablicy zapobiegnie jej podzieleniu z powodu podziału na słowa.i wykonaj polecenia jako
(lub) całkowicie użyj
bash
funkcji do uruchomienia polecenia,i wywołaj funkcję jako sprawiedliwą
POSIX
sh
nie ma tablic, więc najbliższe możliwe jest utworzenie listy elementów w parametrach pozycyjnych. Otosh
sposób POSIX-a na uruchomienie programu pocztowegoZauważ, że to podejście może obsługiwać tylko proste polecenia bez przekierowań. Nie obsługuje przekierowań, potoków, pętli for / while, instrukcji if itp
Innym częstym przypadkiem użycia jest praca
curl
z wieloma polami nagłówka i ładunkiem. Zawsze możesz zdefiniować argumenty jak poniżej i wywołaćcurl
na rozszerzonej zawartości tablicyInny przykład,
Teraz, gdy zmienne są zdefiniowane, użyj tablicy do przechowywania argumentów poleceń
a teraz wykonaj poprawnie cytowane rozszerzenie
źródło
Command=('echo aaa | grep a')
i"${Command[@]}"
, mając nadzieję, że działa dosłownie na polecenieecho aaa | grep a
. Tak nie jest. Zastanawiam się, czy można bezpiecznie zastąpićeval
, ale wydaje się, że każde rozwiązanie ma taką samą siłę, jakeval
mogłoby być niebezpieczne. Prawda?Command() { echo aaa | grep a; }
- po której możesz po prostu uruchomićCommand
, lubresult=$(Command)
, lub tym podobne.Przy użyciu tej metody polecenie jest natychmiast oceniane, a jego wartość zwracana jest przechowywana.
To samo z backtick
Używanie eval w
$(...)
testamencie nie spowoduje, że zostanie on później ocenionyUżywając eval, jest oceniany, gdy
eval
jest używanyW powyższym przykładzie, jeśli chcesz uruchomić polecenie z argumentami, umieść je w przechowywanym ciągu
W przypadku skryptów bash rzadko jest to istotne, ale ostatnia uwaga. Uważaj z
eval
. Oceniaj tylko łańcuchy, które kontrolujesz, nigdy ciągi pochodzące od niezaufanego użytkownika lub utworzone na podstawie niezaufanych danych wejściowych użytkownika.źródło
eval $stored_date
może to wystarczyć, gdystored_date
zawiera tylkodate
, aleeval "$stored_date"
jest znacznie bardziej niezawodne. Na przykład uruchomstr=$'printf \' * %s\\n\' *'; eval "$str"
z cudzysłowami i bez cudzysłowów wokół finału"$str"
. :)W przypadku basha zapisz swoje polecenie w ten sposób:
Uruchom polecenie w ten sposób:
źródło
Próbowałem różnych metod:
Wynik:
Jak widać, tylko trzecia
"$@"
dała poprawny wynik.źródło
Zachowaj ostrożność rejestrując zamówienie w:
X=$(Command)
Ten jest nadal wykonywany nawet przed wywołaniem. Aby to sprawdzić i potwierdzić, możesz:
źródło
źródło
Nie jest konieczne przechowywanie poleceń w zmiennych, nawet jeśli będziesz ich później używać. po prostu wykonaj to normalnie. Jeśli przechowujesz w zmiennej, będziesz potrzebować jakiejś
eval
instrukcji lub wywołać niepotrzebny proces powłoki, aby „wykonać zmienną”.źródło
var='*.txt'; find . -name "$var"