I don't know quite how to ask this question and I'm not even sure this is the place to ask it. It seems rather complex and I don't have a full understanding of what is going on. Frankly, that's why I'm posting - to get some help wrapping my head around this. My end goal is to learn, not to solve my overall problem. I want to understand when I can expect to encounter the situation I'm about to describe and why it happens.
I have a perl module which I've been developing. One of the things it does is it detects whether there is input on standard in (whether that's via a pipe or via a redirect (i.e. <
)).
To catch redirects, I employ a few different checks for various cases. One of them is looking for 0r
file descriptors in lsof
output. It works fairly well and I use my module in a lot of scripts without issue, but I have 1 use-case where my script thinks it's getting input on STDIN when it is not - and it has to do with what I'm getting in the lsof output. Here are the conditions I have narrowed down the case to, but these are not all the requirements - I'm missing something. Regardless, these conditions seem to be required, but take my intuition with a hefty grain of salt, because I really don't know how to make it happen in a toy example - I have tried - which is why I know I'm missing something:
- When I run a perl script from within a perl script via backticks, (the inner script is the one that thinks it has been intentionally fed input on STDIN when it has not - though I should point out that I don't know whether it's the parent or child that actually opened that handle)
- An input file is supplied to the inner script call that resides in a subdirectory
The file with the 0r
file descriptor that lsof is reporting is:
/Library/Perl/5.18/AppendToPath
This file does not show up in the lsof output under other conditions. And if I do eof(STDIN)
before and after the lsof
call, the result is 1 each time. -t STDIN
is undefined. fileno(STDIN)
is 0
.
I read about this file here and if I cat it, it has:
>cat /Library/Perl/5.18/AppendToPath
/System/Library/Perl/Extras/5.18
It appears this is a macOS-perl-specific file meant to append to the @INC
perl path, but I don't know if other OS's provide analogous mechanisms.
I'd like to know more about when that file is present/opened and when it's closed. Can I close it? It seems like the file content has already been read in by the interpreter maybe - so why is it hanging around in my script as an open file handle? Why is it on STDIN? What happens in this case when I actually redirect a file in myself? Is the child process somehow inheriting it from the parent under some circumstance I'm unaware of?
UPDATE: I figured out a third (possibly final) requirement needed to make that AppendToPath file handle be open on STDIN during script execution of the child script. It turns out I had a line of code at the top of the parent script (probably added to try and solve a similar problem when I knew even less than I know now about detecting input on STDIN) that was closing STDIN. I commented out that close and everything started working without any need to exclude that weird file (i.e. that file: /Library/Perl/5.18/AppendToPath
no longer shows as open on STDIN in lsof). This was the code I commented out:
close(STDIN) if(defined(fileno(STDIN)) && fileno(STDIN) ne '' &&
fileno(STDIN) > -1);
It had a comment above it that read:
#Prevent the passing of active standard in handles to the calls to the script
#being tested by closing STDIN.
So I was probably learning about standard input detection at the time I wrote that years ago. My module probably ended up using -t STDIN
and -f STDIN
, etc, but I'd switched those out to work around a problem like this one using lsof so I could see better what was going on. So with the current module (using either lsof or my new(/reverted?) streamlined version using -t/-f/-p
works just fine (as intended) when I don't close STDIN in the parent.
However, I would still like to understand why that file is on STDIN in a child process when the parent closes STDIN...
fcntl
from within perl? When I try, I get this: – hepcat72 Aug 11 '21 at 14:44amIPipedTo
andamIRedirectedTo
that were using lsof output. Their bodies are now entirelyreturn(!-t STDIN && -p STDIN)
&return(!-t STDIN && -f STDIN)
respectively. I was already using-t
elsewhere, but this was a great streamline - glad to not use lsof there. However, I still have the same edge case issue. Ie.amIRedirectedTo
returns true because of that file I'm not intentionally opening:/Library/Perl/5.18/AppendToPath
. It is there on STDIN. lsof at least showed me what was causing it. – hepcat72 Aug 11 '21 at 15:41cmd foo
readsfoo
and createsfoo.out
andcmd foo < file
reads stdin and createsfoo.out
? IMO, that's not a good design. The user should be able to decide when they want the input to come from stdin (whether that's a tty, pipe, /dev/null or regular file) or not. Why notcmd --stub=foo < file
vscmd foo
orcmd --input=foo
(where stub defaults to the name of the input file if note specified)? – Stéphane Chazelas Aug 11 '21 at 16:05perldoc -f fcntl
for how to use perl'sfcntl
, though your issue above look like a shell one. – Stéphane Chazelas Aug 11 '21 at 16:06--stub
to mean to ignore STDIN, but I also have a default stub if one is not provided and STDIN has input. I would like to be able to make the decision based on actual input detection rather than the presence of a flag. – hepcat72 Aug 11 '21 at 16:11--input=file
option that should be the cue to read from stdin. That's what most text utilities do for instance.cat file
reads a filecat
reads stdin (whether it's a tty or regular file or pipe). – Stéphane Chazelas Aug 11 '21 at 16:14-t
to ignore tty cases. So really it boils down to me understanding why this file is on STDIN and when to expect it or anything like it. – hepcat72 Aug 11 '21 at 16:17--flag -
to be intentional about reading STDIN. But one option is allowed to read STDIN by default. – hepcat72 Aug 11 '21 at 16:18