2

When I run the following script:

#!/bin/bash
$(pgrep -u ubuntu -f ${1} > /dev/null)
echo "CURRENT_STATUS="${?}

with:

./my_script.sh top

when top is not running, it returns:

CURRENT_STATUS=0

which is odd, as I would expect a 1 exit status from pgrep. When I remove the shebang from the script it works as expected.

Could someone help me understand what's going on here?

This is on an Ubuntu 22.4.1 system. Also note that the $(...) doesn't change the results.

I have noticed that pgrep seems to be matching against itself and that's why it gives a 0 result no matter what process name I feed it.

terdon
  • 234,489
  • 66
  • 447
  • 667
ozpac
  • 33
  • 4
  • Please [edit] your question and tell us your operating system. Why are you using `$()` when all you want is to run a command, is that on purpose? – terdon Jan 23 '23 at 17:38
  • @terdon - I've edited. Thanks. – ozpac Jan 23 '23 at 17:50
  • @ilkkachu this is run from within a bash shell – ozpac Jan 23 '23 at 17:51
  • The `$()` won't change the result, it's just weird and pointless there so I asked in case there were other parts of the script you're not showing. – terdon Jan 23 '23 at 17:58

1 Answers1

4

With the hashbang, the kernel runs the named interpreter passing it the script file name and the arguments you give, i.e. the command line is /bin/bash ./my_script.sh top, and that shows in the process listing with e.g. ps, and similarly pgrep finds it. It matches the keyword top, after all.

Without the hashbang, the kernel-level system call fails, and Bash runs the script itself, internally, and the arguments don't show in the process listing. (The shells do that, some way or another. IIRC it's a POSIX requirement that non-executable files be run through the shell if possible.)

It's easier to test with e.g. these two:

bash$ cat wait.sh
#!/bin/bash
sleep 50

bash$ cat wait2.sh
sleep 50

bash$ ./wait.sh & ./wait2.sh & ps uax |grep wait
ilkkachu  9029  0.0  0.0  15368  3060 pts/21   S    20:01   0:00 /bin/bash ./wait.sh
ilkkachu  9032  0.0  0.0  16964   968 pts/21   S+   20:01   0:00 grep wait

the one without the hashbang doesn't show up.

I think pgrep is smart enough not to find itself, but to help it not find the script, you could use a pattern like '[t]op', which matches top, but not itself. With ./my_script.sh '[t]op', the difference disappears.

The command substitution there is useless, though, just drop it and run pgrep directly.

ilkkachu
  • 133,243
  • 15
  • 236
  • 397
  • Thank you so much, that's an incredibly helpful and concisely explained insight into how scripts are executed. – ozpac Jan 24 '23 at 15:09
  • I've gone for this edit to the script which seems to work and I think is correct by theory too. What do you think? #!/bin/bash; pgrep -ai -u ubuntu -f ${1} | grep -v $0; echo "CURRENT_STATUS="${?} – ozpac Jan 24 '23 at 15:10
  • @ozpac, looks like it might work. Maybe I'd go with `pgrep -i -f "$1" | grep -vxF "$$"`, i.e. looking at the process id of the script instead of the name (to remove any possible name clash, which would likely be rare but really hard to debug). `grep -x` to look at the whole line, so as not to match 1234 in 12345. Also, quote the expansions just for the sake of a good habit :) – ilkkachu Jan 24 '23 at 17:05
  • Good thinking @ikkachu. Thank you again for your help - it's really appreciated. – ozpac Jan 25 '23 at 15:50