Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why do `#!/usr/bin/env var=val command` gets into an infinite loop

In man(1) env it say:

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

So consider print_A.sh:

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

When I run it with ./print_A.sh it hangs.

Running it with strace ./print_A.sh I get the following log, repeating:

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

As commented below, running a command in a hash-bang is not equivalent to running it on the command line, but still, why does it go into an infinite loop?

like image 259
Chen Levy Avatar asked Aug 04 '26 14:08

Chen Levy


1 Answers

There two parts to this answer. One has already been given in a duplicate question. The answers there, however, explain the root cause for the problem, not what is actually going on.

Part 1 - What's causing this?

Hashbang parsing has never been really standardized. Here is a very good writeup by Sven Mascheck, which also includes a table with the behavior for different operating systems.

The table shows that e.g. Linux does all args in one, meaning that #!/usr/bin/env A=b bash executes env with 'A=b bash' as first argument.

Part 2 -- Why the endless loop?

What happens is that, env is executed, it sets the environment variable A='b bash' and then re-executes the original script. This results in the kernel re-interpreting the hashbang again and we get an endless env-exec loop.

After a little thinking, the problem becomes quite obvious:

A file test.sh with first line #!/bin/sh param executes /bin/sh as '/bin/sh' 'param' 'test.sh'. The script name is appended as a new command line parameter (i.e. to argv).

Thus in the example, env is actually executed as /usr/bin/env 'A=b bash'script_name.

env thus does what it's told, sets the variable, and executes script_name. This again starts hashbang interpretation and we got our loop.

like image 171
dhke Avatar answered Aug 07 '26 22:08

dhke



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!