4

I have a bash script named test.sh which I start with startproc. In case I use #!/usr/bin/env bash shebang in test.sh, the /proc/<PID>/cmdline file looks following:

SERVER:~ # cat /proc/29481/cmdline
bash/root/user/test.shSERVER:~ # 
SERVER:~ # 

Now when I change the shebang line to #!/bin/bash, the /proc/<PID>/cmdline file is following:

SERVER:~ # cat /proc/29729/cmdline
/bin/bash/root/user/test.shSERVER:~ # 
SERVER:~ # 

What causes such behavior? Does the content of /proc/<PID>/cmdline file depend on script shebang? The problem is that in case of former option checkproc, killproc or startproc are not able to detect the test.sh service. I use openSUSE 11.4 with sysvinit-tools-2.88-37.47.1.x86_64.

  • Just guesses, but in the first case the kernel calls bash and fills in cmdline, whereas in the second case it does the same with env, but the bash cmdline you are looking at is set by env. – dhag Dec 8 '15 at 18:08
  • In the first case, the kernel sees #! and logically executes /bin/bash /root/user/test.sh. In the second case, env sees bash and logically executes bash /root/user/test.sh. These both refer to the same binary. The contents of /proc/cmdline should basically be equivalent to the process's argv, IIRC, and argv[0] has always been dependent on the invocation, regardless of which binary is actually executing. – Tom Hunt Dec 8 '15 at 18:10
4

Yes, the cmdline will vary depending on exactly what exec* system call was made, and a fully qualified path may be different than what env(1) turns up from its path search.

bash-4.1$ cat aaa
#!/bin/bash
xxd /proc/$$/cmdline
bash-4.1$ cat bbb
#!/usr/bin/env bash
xxd /proc/$$/cmdline
bash-4.1$ ./aaa
0000000: 2f62 696e 2f62 6173 6800 2e2f 6161 6100  /bin/bash../aaa.
bash-4.1$ ./bbb
0000000: 6261 7368 002e 2f62 6262 00              bash../bbb.
bash-4.1$ 

strace shows the particulars:

bash-4.1$ strace ./aaa 2>&1 | grep exec
execve("./aaa", ["./aaa"], [/* 57 vars */]) = 0
bash-4.1$ strace ./bbb 2>&1 | grep exec
execve("./bbb", ["./bbb"], [/* 57 vars */]) = 0
execve("/sbin/bash", ["bash", "./bbb"], [/* 57 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/sbin/bash", ["bash", "./bbb"], [/* 57 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/local/sbin/bash", ["bash", "./bbb"], [/* 57 vars */]) = -1 ENOENT (No such file or directory)
execve("/bin/bash", ["bash", "./bbb"], [/* 57 vars */]) = 0
bash-4.1$ 

If this is a linux-only script, then the location of bash will likely not vary (exception: there's a software depot of some sort that includes bash, elsewhere). Therefore, using the fully qualified path probably makes the most sense, as this avoids env(1) flailing around in a path search, and gains compatibility with the init tools.

| improve this answer | |
1

The command line of a process consists of the elements of the argv parameter of the execve system call. This parameter is an array numbered from 0, where elements number 1 through n are the arguments passed when invoking the command, and element 0 is chosen by the shell or other program that issues the execve call. Conventionally, element 0 is the string used to designate the command.

Shebang lines are processed by the kernel. The kernel inserts an argument 0 which is the path specified after the #! magic prefix. So if you run /root/user/test.sh with the one argument foo from startproc, then startproc makes an execve call with two arguments 0=/root/user/test.sh, 1=foo. When /root/user/test.sh starts with /bin/bash, the kernel sees the shebang in /root/user/test.sh and rewrites the argument list to 0=/bin/bash, 1=/root/user/test.sh, 2=foo.

If the shebang line is #!/bin/env bash, then the kernel inserts two items in the argument list: the program and the argument. (Linux is limited to a single argument here.) So in this case the invocation is transformed into 0=/bin/env, 1=bash, 2=/root/user/test.sh, 3=foo. The env program does its job and issues a new execve system call with 0=bash (respecting the convention that argument 0 is the path used to designate the command), 1=/root/user/test.sh, 2=foo.

In all cases, /root/user/test.sh will be the first argument of the process.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.