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
  • [PATCH] builtin: ... pourko--- via Bug reports for the GNU Bourne Again SHell
    • Re: [PATCH] ... Pourko via Bug reports for the GNU Bourne Again SHell
      • Re: [PAT... Mike Jonkmans
        • Re: ... Pourko via Bug reports for the GNU Bourne Again SHell
        • Re: ... Pourko via Bug reports for the GNU Bourne Again SHell
        • Re: ... Pourko via Bug reports for the GNU Bourne Again SHell
        • Re: ... Grisha Levit
          • ... Mike Jonkmans
          • ... Pourko via Bug reports for the GNU Bourne Again SHell

Reply via email to