evali execoba są wbudowane w polecenia bash (1), które wykonują polecenia.
Widzę też, że execma kilka opcji, ale czy to jedyna różnica? Co dzieje się z ich kontekstem?
evali execoba są wbudowane w polecenia bash (1), które wykonują polecenia.
Widzę też, że execma kilka opcji, ale czy to jedyna różnica? Co dzieje się z ich kontekstem?
Odpowiedzi:
evali execsą zupełnie innymi zwierzętami. (Oprócz faktu, że oba będą uruchamiać polecenia, ale robi to wszystko, co robisz w powłoce).
$ help exec
exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...]
Replace the shell with the given command.
To exec cmd, co robi, jest dokładnie takie samo, jak tylko uruchamianie cmd, z tą różnicą, że bieżąca powłoka jest zastępowana poleceniem, zamiast uruchamiania osobnego procesu. Uruchamianie say /bin/lswywoła wewnętrznie fork()proces potomny, a następnie exec()proces potomny /bin/ls. exec /bin/lsz drugiej strony nie rozwidli się, a jedynie zastępuje powłokę.
Porównać:
$ bash -c 'echo $$ ; ls -l /proc/self ; echo foo'
7218
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7219
foo
z
$ bash -c 'echo $$ ; exec ls -l /proc/self ; echo foo'
7217
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7217
echo $$wypisuje PID powłoki, którą uruchomiłem, a listing /proc/selfdaje nam PID tego, lsktóry został uruchomiony z powłoki. Zwykle identyfikatory procesów są różne, ale execw powłoce lsmają ten sam identyfikator procesu. Ponadto następujące polecenie execnie zostało uruchomione, ponieważ powłoka została zastąpiona.
Z drugiej strony:
$ help eval
eval: eval [arg ...]
Execute arguments as a shell command.
evaluruchomi argumenty jako polecenie w bieżącej powłoce. Innymi słowy eval foo barto to samo co sprawiedliwe foo bar. Ale zmienne zostaną rozszerzone przed wykonaniem, abyśmy mogli wykonywać polecenia zapisane w zmiennych powłoki:
$ unset bar
$ cmd="bar=foo"
$ eval "$cmd"
$ echo "$bar"
foo
To będzie nie utworzy proces potomny, więc zmienna jest ustawiona w bieżącej powłoki. (Oczywiście eval /bin/lsstworzy proces potomny, tak samo jak zwykły stary /bin/ls).
Lub możemy mieć polecenie, które wyprowadza polecenia powłoki. Uruchamianie ssh-agenturuchamia agenta w tle i generuje wiązkę przypisań zmiennych, które mogą być ustawione w bieżącej powłoce i używane przez procesy potomne ( sshpolecenia, które uruchomisz). Dlatego ssh-agentmożna rozpocząć od:
eval $(ssh-agent)
Bieżąca powłoka pobierze zmienne dla innych poleceń do dziedziczenia.
Oczywiście, jeśli zmienna cmdzawiera coś podobnego rm -rf $HOME, to uruchamianie eval "$cmd"nie byłoby czymś, co chciałbyś zrobić. Nawet takie rzeczy jak zmiana dowodzenia wewnątrz łańcucha będą przetwarzane, więc trzeba naprawdę mieć pewność, że dane wejściowe evalsą bezpieczne przed użyciem.
Często można uniknąć, evala nawet przypadkowo pomieszać kod i dane w niewłaściwy sposób.
evalw pierwszej kolejności również do tej odpowiedzi. Rzeczy takie jak pośrednio modyfikujące zmienne można wykonać w wielu powłokach poprzez declare/ typeset/ namerefi rozszerzenia takie jak ${!var}, więc użyłbym ich zamiast, evalchyba że naprawdę musiałem tego unikać.
execnie tworzy nowego procesu. To zastępuje bieżący proces z nową komendę. Jeśli zrobiłeś to w wierszu poleceń, to skutecznie zakończy sesję powłoki (a może wylogujesz się lub zamkniesz okno terminala!)
na przykład
ksh% bash
bash-4.2$ exec /bin/echo hello
hello
ksh%
Oto jestem ksh(moja normalna powłoka). Zaczynam, basha potem w środku exec /bin/echo. Widzimy, że wróciłem kshpóźniej, ponieważ bashproces został zastąpiony przez /bin/echo.
execsłuży do zastąpienia bieżącego procesu powłoki nowymi i obsługi przekierowań / deskryptorów plików, jeśli nie podano żadnej komendy. evalsłuży do oceny ciągów jako poleceń. Oba mogą być użyte do zbudowania i wykonania polecenia z argumentami znanymi w czasie wykonywania, ale execoprócz wykonywania poleceń zastępuje proces bieżącej powłoki.
Składnia:
exec [-cl] [-a name] [command [arguments]]
Zgodnie z instrukcją, jeśli istnieje polecenie określone to wbudowane
... zastępuje powłokę. Nie jest tworzony nowy proces. Argumenty stają się argumentami do dowodzenia.
Innymi słowy, jeśli działałeś bashz PID 1234 i jeśli miałbyś działać exec top -u rootw tej powłoce, toppolecenie będzie miało PID 1234 i zastąpi proces powłoki.
Gdzie to jest przydatne? W czymś znanym jako skrypty opakowujące. Takie skrypty budują zestawy argumentów lub podejmują określone decyzje dotyczące zmiennych, które mają zostać przekazane do środowiska, a następnie wykorzystują je execdo zastąpienia dowolną określoną komendą i oczywiście podając te same argumenty, które zbudował skrypt opakowania.
Instrukcja podaje również, że:
Jeśli polecenie nie zostanie określone, wszelkie przekierowania zostaną zastosowane w bieżącej powłoce
To pozwala nam przekierowywać wszystko z bieżących strumieni wyjściowych powłok do pliku. Może to być przydatne do rejestrowania lub filtrowania, gdzie nie chcesz widzieć stdoutpoleceń, a jedynie stderr. Na przykład tak:
bash-4.3$ exec 3>&1
bash-4.3$ exec > test_redirect.txt
bash-4.3$ date
bash-4.3$ echo "HELLO WORLD"
bash-4.3$ exec >&3
bash-4.3$ cat test_redirect.txt
2017年 05月 20日 星期六 05:01:51 MDT
HELLO WORLD
To zachowanie sprawia, że jest to przydatne do logowania się w skryptach powłoki , przekierowywania strumieni do oddzielnych plików lub procesów oraz innych zabawnych rzeczy z deskryptorami plików.
Na poziomie kodu źródłowego przynajmniej dla bashwersji 4.3 execwbudowany jest zdefiniowany w builtins/exec.def. Analizuje otrzymane polecenia, a jeśli takie istnieją, przekazuje rzeczy do shell_execve()funkcji zdefiniowanej w execute_cmd.cpliku.
Krótko mówiąc, istnieje rodzina execpoleceń w języku programowania C i shell_execve()jest to po prostu funkcja otoki execve:
/* Call execve (), handling interpreting shell scripts, and handling
exec failures. */
int
shell_execve (command, args, env)
char *command;
char **args, **env;
{
Ręczne ustawienia bash 4.3 (podkreślenie dodane przeze mnie):
Argumenty są odczytywane i łączone w jedno polecenie. To polecenie jest następnie odczytywane i wykonywane przez powłokę , a jego status wyjścia jest zwracany jako wartość eval.
Zauważ, że nie ma zamiany procesu. W przeciwieństwie do tego, execgdzie celem jest symulacja execve()funkcjonalności, evalwbudowany służy tylko do „oceny” argumentów, tak jakby użytkownik wpisał je w wierszu poleceń. W związku z tym tworzone są nowe procesy.
Gdzie to może być przydatne? Jak zauważył Gilles w tej odpowiedzi : „... eval nie jest używane bardzo często. W niektórych powłokach najczęstszym zastosowaniem jest uzyskanie wartości zmiennej, której nazwa jest znana dopiero po uruchomieniu”. Osobiście użyłem go w kilku skryptach na Ubuntu, gdzie konieczne było wykonanie / ocena polecenia w oparciu o konkretny obszar roboczy, z którego użytkownik aktualnie korzysta.
Na poziomie kodu źródłowego jest on zdefiniowany builtins/eval.defi przekazuje przeanalizowany ciąg wejściowy do evalstring()funkcji.
Między innymi evalmoże przypisywać zmienne, które pozostają w bieżącym środowisku wykonywania powłoki, podczas gdy execnie może:
$ eval x=42
$ echo $x
42
$ exec x=42
bash: exec: x=42: not found
tworząc nowy proces potomny, uruchom argumenty i zwróć status wyjścia.
Oh, co? Chodzi o evalto, że w żaden sposób nie tworzy ono procesu potomnego. Jeśli zrobię
eval "cd /tmp"
w powłoce, a następnie bieżąca powłoka zmieni katalog. Ani też nie exectworzy nowego procesu potomnego, zamiast tego zmienia bieżący plik wykonywalny (czyli powłokę) dla danego; identyfikator procesu (i otwarte pliki i inne rzeczy) pozostają takie same. W przeciwieństwie do eval, an execnie powróci do powłoki wywołującej, chyba że execsama zawiedzie z powodu niemożności znalezienia lub załadowania pliku wykonywalnego lub śmierci w wyniku problemów z rozszerzaniem argumentów.
evalw zasadzie interpretuje swoje argumenty jako ciąg po konkatenacji, mianowicie wykona dodatkową warstwę interpretacji symboli wieloznacznych i podziału argumentów. execnic takiego nie robi.
Ocena
Te prace:
$ echo hi
hi
$ eval "echo hi"
hi
$ exec echo hi
hi
Nie powodują one jednak:
$ exec "echo hi"
bash: exec: echo hi: not found
$ "echo hi"
bash: echo hi: command not found
Zastąp proces obrazu
Ten przykład pokazuje, jak execzastępuje obraz procesu wywoływania:
# Get PID of current shell
sh$ echo $$
1234
# Enter a subshell with PID 5678
sh$ sh
# Check PID of subshell
sh-subshell$ echo $$
5678
# Run exec
sh-subshell$ exec echo $$
5678
# We are back in our original shell!
sh$ echo $$
1234
Zauważ, że exec echo $$działał z PID podpowłoki! Co więcej, po jego zakończeniu wróciliśmy do naszej oryginalnej sh$powłoki.
Z drugiej strony, evalczy nie zastąpić obraz procesu. Raczej uruchamia podane polecenie, tak jak normalnie w samej powłoce. (Oczywiście, jeśli uruchomisz polecenie wymagające odrodzenia procesu ... robi to po prostu!)
sh$ echo $$
1234
sh$ sh
sh-subshell$ echo $$
5678
sh-subshell$ eval echo $$
5678
# We are still in the subshell!
sh-subshell$ echo $$
5678
exec)