I tried to make the title succinct, but couldn't think of a way to totally encapsulate the question. Here's what I want to do: when running a perl script, I want to know if there are any other processes other than 'myself' that have a connection to the same STDERR stream (in the current tty or potentially appended/redirected to the same STDERR file) and if so, my goal is to be able to tell the difference between my STDERR errors/warnings/verbose messages and any others from other concurrently running programs. When I detect this as a possibility, I want to prepend my script name to all messages I print to STDERR so that the user knows which process the message came from (or at least which ones were from a script I wrote). At the moment, I don't care how I accomplish this or whether a solution encapsulates all possibilities - for now let's call it a proof of concept that captures a reasonable number of use-cases for a decent default behavior. And I am attempting to do this via system calls.
There are 2 scenarios that I wish to account for: messages from sibling processes run concurrently in a piped series of commands and messages from other child processes run serially by the same parent script. I'm not going to try to handle the case of background processes with a handle on the same STDERR stream, but if those are captured, that would be a bonus.
I believe I have a pretty good handle on the first scenario: sibling processes. I can get all the PIDs of any sibling processes using pgrep -P $ppid
. The simple existence of sibling processes is enough to warrant the script-name prepend to my messages, but I could even go a step further and use lsof to find out if they have their stderr going to the same STDERR stream as myself - if for example they are running in the background and have their STDERR going to the same TTY (inferred by file descriptor '2'). I could even check if I'm a part of a series of piped commands with perl's -p or -t functions (though I do not want to prepend in the event that a file has been redirected in or out using '<' or '>', so -p or -t alone won't work). Regardless, I am satisfied with how to handle concurrently running sibling processes.
The scenario that I haven't sufficiently figured out is serially run processes controlled by a parent script. My idea is to try and make a reasonable guess as to whether my parent process is an interactive shell or whether it's a script.
I've tried reading up on lsof to figure out if there was a way to do this. I was sort of guessing that any errors from an interactive shell/terminal would not be printed to the same STDERR stream as my script, but apparently they do, so I can't simply look for whether any output is going to my tty from the parent to distinguish the 2 scenarios.
I created a one-liner to use for testing in different contexts:
perl -e '$ppid=getppid();print("lsof -p $ppid:\n",`lsof -p $ppid`)'
I ran it in 2 contexts: one directly on the command line and the other from inside a shell script. The only difference I could see in the lsof run on the parent's PID was that when run on the command line, there were 5 read/write connections to the tty and when run from in the shell script, there were 3 connections to the tty and one read-only read of the shell script. None of the file descriptors in any case were 0, 1, or 2.
Interactive shell:
...
tcsh 87854 me 15u CHR 16,2 0t3625 13083 /dev/ttys002
tcsh 87854 me 16u CHR 16,2 0t3625 13083 /dev/ttys002
tcsh 87854 me 17u CHR 16,2 0t3625 13083 /dev/ttys002
tcsh 87854 me 18u CHR 16,2 0t3625 13083 /dev/ttys002
tcsh 87854 me 19u CHR 16,2 0t3625 13083 /dev/ttys002
Inside shell script:
tcsh 89384 me 16r REG 1,3 363 19758500 /whatever/tmpdelete2.tcsh
tcsh 89384 me 17u CHR 16,2 0t4721 13083 /dev/ttys002
tcsh 89384 me 18u CHR 16,2 0t4721 13083 /dev/ttys002
tcsh 89384 me 19u CHR 16,2 0t4721 13083 /dev/ttys002
So my question is: is there enough information here to reasonably infer whether or not the parent process is an interactive shell or not?
Could I reasonably infer from the parent process's REGular read-only read from a file that the parent process is not an interactive terminal session? Or could I infer from the presence of 5 read/write connections to the tty that the parent is an interactive shell?
To slightly test this idea, I tried putting a sleep 1
on the line above my one-liner to see if the read of the script would go away from the lsof output at some point during a long script execution. I also tried redirecting into and out from the shell script when I ran it.
In my use case, I can tell the difference, but I'm not sure about how other shells behave. I'm really only concerned about shell scripts. I can't think of any reasonable scenario where someone calls a perl script from inside anything else...