4

If I run this command in a script it doesn't produce output except header from the ps:

 # Taken from Advanced Bash Usage on youtube:

 echo "$(echo "$(echo "$(echo "$(ps wwf -s $$)")")")"

This is the output:

$./testing.bash
  PID TTY      STAT   TIME COMMAND

but here it is run in a terminal which produces the expected output:

$echo "$(echo "$(echo "$(echo "$(ps wwf -s $$)")")")"
  PID TTY      STAT   TIME COMMAND
18289 pts/4    Ss+    0:00 /bin/bash
17917 pts/4    S+     0:00  \_ /bin/bash
17918 pts/4    S+     0:00      \_ /bin/bash
17919 pts/4    S+     0:00          \_ /bin/bash
17920 pts/4    S+     0:00              \_ /bin/bash
17921 pts/4    R+     0:00                  \_ ps wwf -s 18289

QUESTION:

Can you explain the difference and show me the right way to do this?

I've tried a lot of things and searched google for 4 hours, if you need I can list what I tried but I don't think that's relevant here.

$echo $SHELL
/bin/bash

and:

$head -1 testing.bash 
#!/bin/bash
  • 2
    See also: https://unix.stackexchange.com/questions/18166/what-are-session-leaders-in-ps – muru Jun 29 '19 at 13:11

1 Answers1

10

ps' -s sessionid option is to select processes based on their session id.

You can use ps -j to see the session id of processes. Sessions along with process groups are generally used for shell job control (hence the -j).

Your terminal emulator creates a new session with the process that it then reuses to execute your preferred shell. So, in a terminal, the session id will generally be the same as the pid of that shell.

So, if you run ps -j -s "$$" in that shell, you'll get the processes in the session because "$$" happens to be the same as the session id.

If you run that command in any other shell (like the shell that is executed in a child process to interpret your testing script), any shell that is not a session leader, you'll get nothing because there's no session with an id that corresponds to the pid of that shell.

$ ps -j -s "$$"
  PID  PGID   SID TTY          TIME CMD
 7239  7239  7239 pts/7    00:00:00 zsh
21002 21002  7239 pts/7    00:00:00 ps

$$ is 7239, the session leader. So that ps -j -s 7239 gives me all the processes in that session.

$ sh -xc 'ps -j -s "$$"; ps -j -p "$$"'
+ ps -j -s 21044
  PID  PGID   SID TTY          TIME CMD
+ ps -j -p 21044
  PID  PGID   SID TTY          TIME CMD
21044 21044  7239 pts/7    00:00:00 sh

The first ps command returns nothing because, as the second ps shows, there's no session with id 21044 as the process of id 21044 is not a session leader. The session leader is still 7239, the shell started by the terminal emulator.

$ sh -xc 'ps -j -s "$(($(ps -o sid= -p "$$")))"'
+ ps -o sid= -p 21215
+ ps -j -s 7239
  PID  PGID   SID TTY          TIME CMD
 7239  7239  7239 pts/7    00:00:00 zsh
21215 21215  7239 pts/7    00:00:00 sh
21217 21215  7239 pts/7    00:00:00 ps

Now, we see all the processes in the session. We used ps -o sid= -p "$$" to get the id of the session that $$ belongs to.

  • So would one possible "right way to do this" be to execute the script with setsid e.g. setsid -w ./testing.bash? – steeldriver Jun 29 '19 at 13:28
  • @steeldriver, yes that would create a new session with the shell interpreting the script being the session leader. You could also use script or xterm or any other terminal emulator or run it over ssh that would also create a new session. Or you could use -G instead of -s to get the processes in the job instead of the session (which would work as long as $$ is the process group leader which would generally be the case if the script is started on its one by an interactive shell) – Stéphane Chazelas Jun 29 '19 at 13:32