2016-09-05 12 views
6

Kiedy zrobić:dziwne zachowanie w bash (i ewentualnie inne muszle?)

/bin/bash -c 'cat /proc/$$/cmdline' 

Wyjście pojawia się:

cat/proc/25050/cmdline 

co następuje wyjście Spodziewałem się:

/bin/bash -c 'cat /proc/$$/cmdline' 

Z drugiej strony, gdy wykonuję:

/bin/bash -c 'echo $$; cat /proc/$$/cmdline' 

uzyskać oczekiwany wynik, który jest:

28259 
/bin/bash-cecho $$; cat /proc/$$/cmdline 

Wygląda $$ jest PID kota zamiast PID bash/sh jest.
Dlaczego tak jest?
Czy powłoka wykonuje jakieś parsowanie i zastępuje styl execve()? Jeśli tak, to w jaki sposób zna on PID kota, zanim jeszcze go zastąpi?

Odpowiedz

4

Aby zrozumieć ten problem, trzeba się dowiedzieć jak bash wykonuje polecenia przekazywane do niej w wierszu poleceń. Kluczową kwestią jest to, że jeśli komenda jest na tyle prosty, nie ma fork (lub clone lub coś podobnego).

$ strace -f -e clone,execve /bin/bash -c 'cat /proc/$$/cmdline' 
execve("/bin/bash", ["/bin/bash", "-c", "cat /proc/$$/cmdline"], [/* 80 vars */]) = 0 
execve("/bin/cat", ["cat", "/proc/2942/cmdline"], [/* 80 vars */]) = 0 
cat/proc/2942/cmdline+++ exited with 0 +++ 
$ 

OTOH jeżeli polecenie jest bardziej skomplikowana, bash widelce:

$ strace -f -e clone,execve /bin/bash -c 'echo $$; cat /proc/$$/cmdline' 
execve("/bin/bash", ["/bin/bash", "-c", "echo $$; cat /proc/$$/cmdline"], [/* 80 vars */]) = 0 
2933 
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7ff64e6779d0) = 2934 
Process 2934 attached 
[pid 2934] execve("/bin/cat", ["cat", "/proc/2933/cmdline"], [/* 80 vars */]) = 0 
/bin/bash-cecho $$; cat /proc/$$/cmdline[pid 2934] +++ exited with 0 +++ 
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=2934, si_uid=1000, si_status=0, si_utime=0, si_stime=0} --- 
+++ exited with 0 +++ 
$ 

Wydaje się, $$ jest PID kota zamiast PID bash/sh jest.

To rzeczywiście oba. bashexecve s cat bezpośrednio, więc jeden staje się drugim.

Aby zrozumieć, co dokładnie jest potrzebne do no-widelec zachowania, musimy spojrzeć na kod źródłowy. Jest to komentarz:

 /* 
     * IF 
     * we were invoked as `bash -c' (startup_state == 2) AND 
     * parse_and_execute has not been called recursively AND 
     * we're not running a trap AND 
     * we have parsed the full command (string == '\0') AND 
     * we're not going to run the exit trap AND 
     * we have a simple command without redirections AND 
     * the command is not being timed AND 
     * the command's return status is not being inverted 
     * THEN 
     * tell the execution code that we don't need to fork 
     */ 

Source

+0

Widziałem to wyjście kiedy straced równie dobrze. Gdziekolwiek stwierdzi, że polecenie nie jest skomplikowane i po tokenizacji określa, że ​​nie musi odpalać żadnego podprocesu (w wyniku ';' lub '||' lub '&&' lub '|') to po prostu ' execve's przekazać polecenie do niego. W przeciwnym razie 'clone's, a następnie' execve's w potomku/klonie. To, czego nie rozumiem, to dlaczego. – ffledgling

+0

@ffledgling Nie jest komentarz w źródle 'IF byliśmy wywołany jako \' bash -c”(startup_state == 2) I parse_and_execute nie została wywołana rekurencyjnie i nie jesteśmy prowadzenie pułapkę i mamy przeanalizował pełną komendę (ciąg == '\ 0') ORAZ nie będziemy uruchamiać pułapki wyjściowej ORAZ mamy proste polecenie bez przekierowań ORAZ komenda nie jest odmierzana ORAZ status powrotu komendy nie jest jest odwrócony THEN THEN powiedzieć kod wykonawczy, że nie trzeba rozwidlenia ' –

+0

Czy mógłbyś dodać to do odpowiedzi i link do odpowiedniej linii w oficjalnym źródłem? – ffledgling