2015-10-14 6 views
3

В man(1) env это сказать:почему `#/USR/бен/окр вар = значение command` попадает в бесконечный цикл

env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...] 

Так считают print_A.sh:

#!/usr/bin/env A=b bash 
echo A is $A 

Когда я бегу он с ./print_A.sh он висит.

Запуск его strace ./print_A.sh я получаю следующий журнал, повторяя:

execve("/path/to/print_A.sh", ["/path/to/print_A.sh"...], [/* 114 vars */]) = 0 
uname({sys="Linux", node="my-host", ...}) = 0 
brk(0)         = 0x504000 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2a95556000 
access("/etc/ld.so.preload", R_OK)  = -1 ENOENT (No such file or directory) 
open("/etc/ld.so.cache", O_RDONLY)  = 3 
fstat(3, {st_mode=S_IFREG|0644, st_size=171528, ...}) = 0 
mmap(NULL, 171528, PROT_READ, MAP_PRIVATE, 3, 0) = 0x2a95557000 
close(3)        = 0 
open("/lib64/tls/libc.so.6", O_RDONLY) = 3 
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240\305\30100\0\0\0"..., 832) = 832 
fstat(3, {st_mode=S_IFREG|0755, st_size=1641152, ...}) = 0 
mmap(0x3030c00000, 2330696, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x3030c00000 
mprotect(0x3030d30000, 1085512, PROT_NONE) = 0 
mmap(0x3030e2f000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x12f000) = 0x3030e2f000 
mmap(0x3030e35000, 16456, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x3030e35000 
close(3)        = 0 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2a95581000 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2a95582000 
mprotect(0x3030e2f000, 16384, PROT_READ) = 0 
mprotect(0x3030b14000, 4096, PROT_READ) = 0 
arch_prctl(ARCH_SET_FS, 0x2a95581b00) = 0 
munmap(0x2a95557000, 171528)   = 0 
brk(0)         = 0x504000 
brk(0x525000)       = 0x525000 
open("/usr/lib/locale/locale-archive", O_RDONLY) = 3 
fstat(3, {st_mode=S_IFREG|0644, st_size=48529088, ...}) = 0 
mmap(NULL, 48529088, PROT_READ, MAP_PRIVATE, 3, 0) = 0x2a95583000 
close(3)        = 0 

Как прокомментировал ниже, запустив команду в хэш-паф не эквивалентно запустить его в командной строке, но все-таки, почему он переходит в бесконечный цикл?

+1

Аргументы Hashbang разделяются только на два слова, поэтому это действительно 'env A = 'b python2.7''. Это, однако, зависит от системы и в прошлом было обработано по-разному (я помню, что фиксация FreeBSD меняла это, [здесь обсуждается Linux] (https://lkml.org/lkml/2004/2/16/74). Вы можете добиться того же результата с помощью '#!/Usr/bin/env a = b', который также (по крайней мере, здесь) петли навсегда. Не совсем уверен, почему это так. – dhke

+1

Также: [почему установка начальной среды с использованием env останавливает запуск моего скрипта Python на Ubuntu] (http://stackoverflow.com/questions/10217199/why-does-setting-an-initial-environment-using-env -stall-the-launch-of-my-python), который, однако, не отвечает на фактический вопрос ** почему **, что происходит. – dhke

ответ

2

На этот ответ две части. Один из них уже приведен в duplicate question. Однако ответы там объясняют основную причину проблемы, а не то, что происходит на самом деле.

Часть 1 - Что вызывает это?

Разбор Hashbang никогда не был стандартизирован. Here - очень хорошая запись Sven Mascheck, в которую также входит таблица с поведением для разных операционных систем.

В таблице показано, что, например, Linux делает все аргументы в одном, что означает, что #!/usr/bin/env A=b bash выполняет env с 'A=b bash' в качестве первого аргумента.

Часть 2 - Почему бесконечная петля?

Выполнено, что выполнено env, оно устанавливает переменную окружения A='b bash', а затем повторно выполняет оригинальный скрипт. Это приводит к тому, что ядро ​​повторно интерпретирует hashbang снова, и мы получаем бесконечный цикл env -exec.

После небольшого мышления, проблема становится совершенно очевидна:

Файл test.sh с первой строкой #!/bin/sh param выполняет /bin/sh в '/bin/sh' 'param' 'test.sh'. Имя сценария добавляется как новый параметр командной строки (то есть до argv).

Таким образом, в примере, env фактически выполняется как /usr/bin/env 'A=b bash'script_name.

env, таким образом, делает то, что он сказал, устанавливает переменную, и выполняет script_name. Это снова начинает интерпретацию hashbang, и мы получили наш цикл.