Do czego służy polecenie „exec”?

108

Nie rozumiem polecenia bash exec. Widziałem, jak był używany w skryptach do przekierowywania wszystkich danych wyjściowych do pliku (jak pokazano w tym ). Ale nie rozumiem, jak to działa ani co ogólnie robi. Przeczytałem strony podręcznika, ale ich nie rozumiem.

becko
źródło
Czy znasz już proces ?
fkraiem
1
@fkraiem co masz na myśli?
becko
Przepraszam, miałem na myśli „co”. : p Ale odpowiedź wydaje się nie.
fkraiem
Ale tak naprawdę twój skrypt używa execw specjalny sposób, który można wyjaśnić znacznie prościej, napiszę odpowiedź.
fkraiem
1
Powiązane: unix.stackexchange.com/q/296838/85039
Sergiy Kolodyazhnyy

Odpowiedzi:

88

man bash mówi:

exec [-cl] [-a name] [command [arguments]]
      If command is specified, it replaces the shell.  No new  process
      is  created.  The arguments become the arguments to command.  If
      the -l option is supplied,  the  shell  places  a  dash  at  the
      beginning  of  the  zeroth  argument passed to command.  This is
      what login(1) does.  The -c option causes command to be executed
      with  an empty environment.  If -a is supplied, the shell passes
      name as the zeroth argument to the executed command.  If command
      cannot  be  executed  for  some  reason, a non-interactive shell
      exits, unless the execfail shell option  is  enabled.   In  that
      case,  it returns failure.  An interactive shell returns failure
      if the file cannot be executed.  If command  is  not  specified,
      any  redirections  take  effect  in  the  current shell, and the
      return status is 0.  If there is a redirection error, the return
      status is 1.

Ważne są dwa ostatnie wiersze: jeśli uruchomisz execsię sam, bez polecenia, po prostu sprawi, że przekierowania zostaną zastosowane do bieżącej powłoki. Prawdopodobnie wiesz, że po uruchomieniu command > filedane wyjściowe commandsą zapisywane w miejscu filezamiast w terminalu (nazywa się to przekierowaniem ). Jeśli uruchomisz exec > filezamiast tego, przekierowanie dotyczy całej powłoki: Wszelkie dane wyjściowe wytworzone przez powłokę są zapisywane filezamiast do terminala. Na przykład tutaj

bash-3.2$ bash
bash-3.2$ exec > file
bash-3.2$ date
bash-3.2$ exit
bash-3.2$ cat file
Thu 18 Sep 2014 23:56:25 CEST

Najpierw zaczynam nową bashpowłokę. Następnie w tej nowej powłoce uruchamiam exec > file, aby wszystkie dane wyjściowe były przekierowywane do file. Rzeczywiście, po tym uruchamiam, dateale nie otrzymuję danych wyjściowych, ponieważ dane wyjściowe są przekierowywane do file. Następnie zamykam moją powłokę (aby przekierowanie przestało obowiązywać) i widzę, że filerzeczywiście zawiera ona wynik datepolecenia, które uruchomiłem wcześniej.

fkraiem
źródło
42
To tylko częściowe wyjaśnienie. execsłuży również do zastąpienia bieżącego procesu powłoki poleceniem, dzięki czemu rodzic idzie drogą, a dziecko posiada pid. Dotyczy to nie tylko przekierowania. Dodaj tę informację
Sergiy Kolodyazhnyy
3
Po komentarzu @ SergiyKolodyazhnyy, przykładem, który właśnie spotkałem, który doprowadził mnie do tej strony, jest docker-entrypoint.sh, gdzie po wykonaniu różnych akcji na konfiguracji nginx, ostatnia linia skryptu exec nginx <various nginx arguments>. Oznacza to, że nginx przejmuje pid skryptu bash, a teraz nginx jest głównym działającym procesem kontenera, a nie skryptem. Zakładam, że to tylko ze względu na czystość, chyba że ktoś inny zna bardziej konkretny powód?
Luke Griffiths
1
@Luke Nazywa się to „skryptem opakowania”. Przykładem tego jest również to, że gnome-terminalprzynajmniej 14.04 miał skrypt otoki do ustawiania argumentów. I to jest ich jedyny cel, naprawdę - sztuka i środowisko. Innym przypadkiem byłoby posprzątanie - zabicie poprzedniej instancji procesu i uruchomienie nowej
Sergiy Kolodyazhnyy
inny execprzykład podobny do nginxprzykładu podanego przez @LukeGriffiths jest ~/.vnc/xstartupskrypt, który vncserverwykorzystuje się do konfigurowania serwera VNC proces, a następnie exec gnome-sessionalbo exec startkdei tak dalej.
Trevor Boyd Smith
@LukeGriffiths, głównym powodem execskryptu uruchamiania kontenera jest to, że PID 1, ENTRYPOINT kontenera, ma szczególne znaczenie w Docker. Jest to główny proces, który odbiera sygnały, a gdy już istnieje, kontener również wychodzi. execpo prostu sposób na shusunięcie tego łańcucha poleceń i uczynienie demona głównym procesem kontenera.
kkm
46

exec jest poleceniem o dwóch bardzo różnych zachowaniach, w zależności od tego, czy użyto z nim co najmniej jednego argumentu, czy też nie użyto żadnego argumentu.

  • Jeśli przekazany zostanie co najmniej jeden argument, pierwszy zostanie przyjęty jako nazwa polecenia i execspróbuje wykonać go jako polecenie przekazujące pozostałe argumenty, jeśli takie istnieją, do tego polecenia i zarządza ewentualnymi przekierowaniami.

  • Jeśli polecenie przekazane jako pierwszy argument nie istnieje, bieżąca powłoka, a nie tylko polecenie exec, kończy działanie błędnie.

  • Jeśli polecenie istnieje i jest wykonywalne, zastępuje bieżącą powłokę. Oznacza to, że jeśli execpojawi się w skrypcie, instrukcje następujące po wywołaniu exec nigdy nie zostaną wykonane (chyba że execsamo jest w podpowłoce). execnigdy nie wraca. Pułapki typu „EXIT” również nie zostaną uruchomione.

  • Jeśli nie zostanie przekazany żaden argument, execsłuży jedynie do przedefiniowania bieżących deskryptorów plików powłoki. Powłoka jest kontynuowana po exec, w przeciwieństwie do poprzedniego przypadku, ale standardowe przekierowanie wejścia, wyjścia, błędu lub dowolnego deskryptora pliku zostało przekierowane.

  • Jeśli niektóre z przekierowań zostaną użyte /dev/null, wszelkie dane wejściowe zwrócą EOF, a wszelkie dane wyjściowe zostaną odrzucone.

  • Możesz zamknąć deskryptory plików, używając -jako źródła lub miejsca docelowego, np exec <&-. Kolejne odczytywanie lub zapisywanie nie powiedzie się.

Oto dwa przykłady:

echo foo > /tmp/bar
exec < /tmp/bar # exec has no arguments, will only affect current shell descriptors, here stdin
cat # simple command that read stdin and write it to stdout

Ten skrypt wyświetli „foo” jako polecenie cat, zamiast czekać na dane wejściowe użytkownika, tak jak w zwykłym przypadku, pobierze dane wejściowe z pliku / tmp / bar, który zawiera foo.

echo foo > /tmp/bar
exec wc -c < /tmp/bar # exec has two arguments, the control flow will switch to the wc command
cat

Ten skrypt wyświetli 4(liczbę bajtów w / tmp / bar) i natychmiast się zakończy. catPolecenie nie zostanie wykonane.

jlliagre
źródło
4
`Niektóre stare posty nigdy się nie zestarzeją ... +1.
Cbhihe
3
Jeśli niektóre przekierowania używają / dev / null, powiązany deskryptor pliku jest zamknięty. Nie, tak naprawdę przekierowuje do / z /dev/null, więc zapisy nadal kończą się powodzeniem, a odczyty zwracają EOF. close(2)na fd spowoduje, że wywołania systemowe odczytu / zapisu zwrócą błędy, a ty zrobisz to exec 2>&-na przykład.
Peter Cordes,
2
Spróbuj sam: exec 3</dev/null; ls -l /proc/self/fdpamiętaj, że fd 3 będzie otwarty tylko do odczytu na / dev / null. Następnie zamknij go ponownie za pomocą exec 3<&-, i zobaczysz ( ls -l /proc/$$/fdponownie), że twój proces powłoki nie ma już FD 3. (zamykanie standardowego wejścia exec <&-może być przydatne w skryptach, ale interaktywnie jest to wylogowanie).
Peter Cordes
@PeterCordes Masz absolutną rację. Odpowiedź zaktualizowana. Dzięki!
jlliagre
1
Ta odpowiedź jest świetna, ponieważ wyjaśnia dwa przypadki użycia execobecnie najbardziej uprzywilejowanej odpowiedzi, mówi tylko o jednym przypadku użycia, a druga odpowiedź g_p mówi tylko o drugim przypadku użycia. Ta odpowiedź jest miła i zwięzła / czytelna w przypadku tak złożonego tematu.
Trevor Boyd Smith
33

Aby zrozumieć exec, musisz najpierw zrozumieć fork. Staram się streścić.

  • Kiedy dojeżdżasz do rozwidlenia drogi, zazwyczaj masz dwie opcje. Programy Linuksa docierają do tego rozwidlenia w drodze, gdy uderzą w fork()wywołanie systemowe.

  • Normalne programy to polecenia systemowe, które istnieją w skompilowanej formie w systemie. Po uruchomieniu takiego programu tworzony jest nowy proces. Ten proces potomny ma takie samo środowisko jak jego rodzic, tylko numer identyfikacyjny procesu jest inny. Ta procedura nazywa się rozwidleniem .

  • Forking umożliwia istniejącemu procesowi rozpoczęcie nowego. Mogą jednak wystąpić sytuacje, w których proces potomny nie jest częścią tego samego programu co proces macierzysty. W tym przypadku execjest używany. exec zastąpi zawartość aktualnie uruchomionego procesu informacjami z pliku binarnego programu.
  • Po procesie rozwidlania przestrzeń adresowa procesu potomnego jest zastępowana nowymi danymi procesu. Odbywa się to poprzez wywołanie exec do systemu.
g_p
źródło
3
czy możesz wyjaśnić, dlaczego execmożna przekierować wyjście skryptu, tak jak w linku, który zamieściłem?
becko
1
Znalazłem to niejasne „Jednak mogą zdarzyć się sytuacje, w których proces potomny nie jest częścią tego samego programu co proces macierzysty”
cdosborn
6

W bash, jeśli zrobisz help exec:

$ help exec
exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...]
    Replace the shell with the given command.

    Execute COMMAND, replacing this shell with the specified program.
    ARGUMENTS become the arguments to COMMAND.  If COMMAND is not specified,
    any redirections take effect in the current shell.

    Options:
      -a name   pass NAME as the zeroth argument to COMMAND
      -c        execute COMMAND with an empty environment
      -l        place a dash in the zeroth argument to COMMAND

    If the command cannot be executed, a non-interactive shell exits, unless
    the shell option `execfail' is set.

    Exit Status:
    Returns success unless COMMAND is not found or a redirection error occurs.

Odpowiedni bit:

If COMMAND is not specified, any redirections take effect in the current shell.

execjest wbudowaną powłoką , która jest odpowiednikiem powłoki execrodziny wywołań systemowych , o których mówi G_P (i których stronom wydaje się, że przeczytałeś). Ma tylko obowiązkową funkcję POSIX wpływania na bieżącą powłokę, jeśli nie podano polecenia.

muru
źródło