Wykonaj polecenie zapisane w zmiennej

11

Mam polecenie zapisane w zmiennej. Udawajmy, że zmienna $ima wartość:

cat -nT index.php |grep 'someregex'

Gdy próbuję wykonać powyższą zmienną przez wpisanie, $inie powiedzie się, ponieważ powłoka próbuje wykonać całą zmienną jako jedno polecenie. Próbowałem także użyć eval($i)i wprowadzić $ibackticks.

Jak mogę wykonać powłokę $itak, jakby była poleceniem? I dlaczego to nie działa tak samo jak.

$i='echo hi'; $i

Czy to dlatego, że musiałem włamać się w pojedyncze cytaty? (Ponieważ nie możesz ich zagnieździć.) Obecnie moim rozwiązaniem jest

echo $i > /foo;  . /foo

Ale nie chcę tworzyć pliku tylko do tego, aby go usunąć później.

Mam na myśli „włamałem się do pojedynczych cytatów:

$i='cat index.php | grep -P '"'"'MYREGEXHERE'"'"
Leathan
źródło
4
Pytanie brzmi: dlaczego polecenie jest przechowywane w zmiennej? Lepiej skompiluj komendę po uruchomieniu, przechowując argumenty opcji w zmiennych lub w tablicy. Czy możesz pokazać cały skrypt, który masz?
slhck
Ten sam główny powód: superuser.com/questions/360966/...
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功

Odpowiedzi:

18

Krótka odpowiedź: patrz BashAQ # 50: Próbuję umieścić polecenie w zmiennej, ale złożone przypadki zawsze zawodzą! .

Długa odpowiedź: wynika to z kolejności, w jakiej bash analizuje wiersz poleceń. W szczególności szuka rzeczy takich jak potoki i przekierowania, zanim rozszerzy wartości zmiennych, i nie wraca i nie szuka ponownie potoków itp. W wartościach rozszerzonych. Zasadniczo zmienne są podstawiane mniej więcej w połowie procesu analizowania, więc wartość jest dopiero w połowie analizowana przed wykonaniem.

Aby rozwiązać ten problem, musisz odpowiedzieć na pytanie @ slhck: dlaczego polecenie jest przechowywane w zmiennej w pierwszej kolejności? Jaki jest faktyczny problem, który próbujesz rozwiązać? W zależności od rzeczywistego celu istnieje wiele możliwych rozwiązań:

  • Nie przechowuj go w zmiennej, po prostu uruchom go bezpośrednio. Przechowywanie poleceń do późniejszego wykorzystania jest trudne, a jeśli tak naprawdę nie potrzebujesz, po prostu nie.

  • Użyj funkcji zamiast zmiennej. W końcu po to są:

    i() { cat -nT index.php |grep 'someregex'; }
    i

    Główną wadą tego jest to, że nie można dynamicznie utworzyć funkcji - nie można warunkowo dołączyć lub wykluczyć kodu z funkcji (chociaż sama funkcja może mieć elementy warunkowe, które są wybierane podczas jej wykonywania).

  • Zastosowanie eval. Należy to uznać za ostateczność, ponieważ łatwo jest uzyskać nieoczekiwane zachowanie. Zasadniczo uruchamia polecenie przez kolejne pełne parsowanie, więc wszystkie potoki itp. Mają pełne znaczenie - ale oznacza to również, że części polecenia, które uważałeś za dane, również zostaną przeanalizowane i być może wykonane. Nazwy plików zawierające metaznaki powłoki (potok, średnik, cytat / apostrof itp.) Mogą mieć dziwne, a czasem niebezpieczne efekty. Jeśli użyjesz eval, przynajmniej podwój cudzysłów, w przeciwnym razie jego zawartość zostanie zasadniczo przeanalizowana półtora razy, z jeszcze dziwniejszym rezultatem.

    i="cat -nT index.php |grep 'someregex'"
    eval "$i"

EDYCJA: Innym standardowym podejściem „zapisz polecenie w zmiennej” jest użycie tablicy zamiast prostej zmiennej. Umożliwia to przechowywanie polecenia ze złożonymi argumentami (np. Zawierającymi spacje) bez problemów, ale nie przechowuje rzeczy takich jak potoki i przekierowania. Zatem podejście tablicowe nie będzie przydatne w tym konkretnym przypadku.

Gordon Davisson
źródło
+1 za evalmetodę. To powstrzymało mnie od zadawania tego samego pytania :). Mam coś takiego, sudo rsync -aAHXi -n --delete-excluded --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/lost+found","/mnt/DATA/*","/var/log/*","/var/swap","/var/cache/apt/archives/*.deb"} -e ssh root@piac_wireless:/ /home/mrx/Docs/RPi/backup/piac/piac_usb-rootże niepoprawne wykonanie wykluczeń.
dentex,
@dentex Zdecydowanie odradzam używanie evaldo tego - istnieje zbyt wiele sposobów, aby się nie udać. Lepszym sposobem na to jest tablica. Zobacz tutaj , tutaj i tutaj dla przykładów.
Gordon Davisson,
Dzieki za sugestie. Spróbuję zaimplementować rozwiązanie z tablicą, aby przekazać listę wykluczeń do rsync.
dentex
4

To powinno po prostu działać:

a="cat -nT index.php |grep 'someregex'"
eval "$a"

Uwaga, która evalwprowadza potencjalne luki w zabezpieczeniach i nieoczekiwane sytuacje, dlatego należy z niej korzystać, o ile w ogóle, ze szczególną ostrożnością.

jlliagre
źródło
Jeśli używasz eval, naprawdę musisz użyć podwójnego cudzysłowu ( eval "$i"), aby uniknąć wyjątkowo dziwnych efektów. Na przykład wypróbuj to za pomocą a="cat -nT index.php |grep ' .* '"(tzn. Wyrażenie regularne powinno pasować do dwóch spacji z dowolną sekwencją znaków między nimi) i zobaczyć, co się właściwie dzieje. Aby uzyskać dodatkowy kredyt, wyjaśnij, dlaczego tak się dzieje.
Gordon Davisson
Dodano podwójne cytaty @ GordonDavisson.
jlliagre
2

Podczas deklarowania zmiennej nie należy używać znaku dolara jako prefiksu.

Spróbuj tego:

i='echo hi'; $i
miq
źródło
1
Zgadza się, ale tak naprawdę nie jest to odpowiedź na pytanie PO.
slhck
to dla mnie PRAWDZIWE: P
nwgat