On Thu, 18 Sep 2025 15:26:16 -0400 Chet Ramey wrote:
> There isn't a good way for a shell script to determine whether or not
> it's in the foreground or background, and whether it is in the same
> process group as the terminal. You could write a C/perl/python program
> that basically does
>
> tpgrp = tcgetpgrp(fd);
> mpgrp = getpgid(0);
> backgroud = tpgrp != mpgrp;
>
> and proceed accordingly, but if you try to read from the terminal or
> set the terminal attributes when you're not in the terminal's process
> group, your process is going to get stopped.
This is exactly what I've been looking for! Thank you!
Now I have a good way to avoid getting stopped, and proceed accordingly.
Attached is the patch that does it.
[...]
-s FILE True if file exists and is not empty.
-S FILE True if file is a socket.
-t FD True if FD is opened on a terminal.
+ -T FD True if FD is opened on a terminal and is readable.
-u FILE True if the file is set-user-id.
-w FILE True if the file is writable by you.
-x FILE True if the file is executable by you.
[...]
Now...
[ -t 0 ] && [ ! -T 0 ]
...means we are running in the background.
Patch (apply with `patch -p0'):
Adds a new file operator to the test command: `-T'
Patch (apply with `patch -p0'):
--- builtins/test.def
+++ ../bash-5.3-patched/builtins/test.def Fri Sep 19 07:25:55 2025
@@ -50,6 +50,7 @@
-s FILE True if file exists and is not empty.
-S FILE True if file is a socket.
-t FD True if FD is opened on a terminal.
+ -T FD True if FD is opened on a terminal and is readable.
-u FILE True if the file is set-user-id.
-w FILE True if the file is writable by you.
-x FILE True if the file is executable by you.
--- test.c
+++ ../bash-5.3-patched/test.c Fri Sep 19 07:41:51 2025
@@ -231,7 +231,7 @@
* term ::=
* '-'('a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'k'|'p'|'r'|'s'|'u'|'w'|'x')
filename
* '-'('G'|'L'|'O'|'S'|'N') filename
- * '-t' [int]
+ * '-'('t'|'T') [int]
* '-'('z'|'n') string
* '-'('v'|'R') varname
* '-o' option
@@ -506,7 +506,7 @@
return (FALSE);
/* the only tricky case is `-t', which may or may not take an argument. */
- if (posixly_correct == 0 && op[1] == 't')
+ if (posixly_correct == 0 && (op[1] == 't' || op[1] == 'T'))
{
advance (0);
if (pos < argc)
@@ -638,6 +638,11 @@
integer_expected_error (arg);
return ((r == (int)r) && isatty ((int)r));
+ case 'T': /* File fd is a terminal? If yes, is it readable? */
+ if (valid_number (arg, &r) == 0)
+ integer_expected_error (arg);
+ return ( (r == (int)r) && isatty ((int)r) && (tcgetpgrp((int)r) ==
getpgid(0)) );
+
case 'n': /* True if arg has some length. */
return (arg[0] != '\0');
@@ -766,7 +771,7 @@
{
case 'a': case 'b': case 'c': case 'd': case 'e':
case 'f': case 'g': case 'h': case 'k': case 'n':
- case 'o': case 'p': case 'r': case 's': case 't':
+ case 'o': case 'p': case 'r': case 's': case 't': case 'T':
case 'u': case 'v': case 'w': case 'x': case 'z':
case 'G': case 'L': case 'O': case 'S': case 'N':
case 'R':
--
2.51.0