LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Software (https://www.linuxquestions.org/questions/linux-software-2/)
-   -   How to I test if a script is run from and interactive shell? (https://www.linuxquestions.org/questions/linux-software-2/how-to-i-test-if-a-script-is-run-from-and-interactive-shell-4175695319/)

mfoley 05-19-2021 05:53 PM

How to I test if a script is run from and interactive shell?
 
I have a script that can be run as a sendmail alias command, or a cron job, or run by a user. I'd like the script to know if it's being run by cron or otherwise in the background versus a user running the command at a bash prompt.

What I've found so far on the web says that if $PS1 exists, it's running from a user command line. However, I created a simple script:
Code:

echo "$PS1" >/tmp/inter
and after running it from the command line /tmp/inter is empty. I've also read that $- will have an "i", so similar script:
Code:

echo $- >/tmp/inter
That gives "hB", no "i".

So, how do I do this?

dannybpng 05-19-2021 07:01 PM

This should do the trick. The -t option checks if the given file descriptor number is a terminal.

Quote:

if [ -t 0 ];then
echo Interactive
fi

mfoley 05-20-2021 01:35 AM

dannybpng: Nope, that doesn't work. If I type the following, manually, at the command line:
Code:

if [ -t 0 ]; then echo interactive; else echo not interactive; fi
It comes back with "interactive". If I put that same line in a bash script and run the bash script it comes back "not interactive".

Other ideas?

More info ...

The bash script I put the test line in is getting stdin piped into it! That's why it comes back "not interactive". Yet I'm running it manually, at an ssh terminal session. So I need another way to figure that out.

shruggy 05-20-2021 02:32 AM

Quote:

Originally Posted by mfoley (Post 6252241)
The bash script I put the test line in is getting stdin piped into it! That's why it comes back "not interactive".

Then test for 1 (stdout) or 2 (stderr).

Reuti 05-20-2021 03:31 AM

Look at the name of the parent process.
 
One could test for the parent of the script in question:
Code:

ps --no-headers -p $PPID -o command
This should output either -bash or something like /usr/sbin/CRON -n (path maybe different on your distribution though).

MadeInGermany 05-20-2021 04:09 AM

The tests in your initial post are okay.
Code:

if [[ $- == *i* ]]; then echo interactive; else echo not interactive; fi
should work in bash/ksh/zsh
Usually testing $PS1 is good enough:
Code:

if [ -n "$PS1" ]; then echo interactive; else echo not interactive; fi

dannybpng 05-20-2021 07:02 AM

If this code is put into a script and run from a cron job, it does work. Running this same script from the prompt still means that there is a connection with a terminal. My guess is that it will also work with sendmail, unless the user is directly interacting with the program.

Code:

if [ -t 0 ];then echo Interactive; fi

Guttorm 05-20-2021 08:48 AM

In default ~/.bashrc there is this:

Code:

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

It checks if $- contains an i or not.

mfoley 05-20-2021 09:16 AM

Quote:

Originally Posted by shruggy (Post 6252254)
Then test for 1 (stdout) or 2 (stderr).

Yes! When piping into a script:
Code:

cat thisfile | testme
having the line "if [ -t 2 ]; then echo interactive ; else echo not interactive ; fi" it comes back back "interactive". Testing for 0 comes back "not interactive". This may be the solution. I'll experiment more.
Quote:

Originally Posted by Reuti (Post 6252263)
One could test for the parent of the script in question:
Code:

ps --no-headers -p $PPID -o command
This should output either -bash or something like /usr/sbin/CRON -n (path maybe different on your distribution though).

Interestingly, I get the following results:

-bash when run manually at the command line
-su when run as shown above inside the testme script at the command line
/usr/sbin/crond -l notice when run from crontab

(both lines) when run from crontab

Quote:

Originally Posted by MadeInGermany (Post 6252282)
The tests in your initial post are okay.
Code:

if [[ $- == *i* ]]; then echo interactive; else echo not interactive; fi
should work in bash/ksh/zsh
Usually testing $PS1 is good enough:
Code:

if [ -n "$PS1" ]; then echo interactive; else echo not interactive; fi

$PS1 is not defined either in the cron case or the piped command line case.
Quote:

Originally Posted by dannybpng (Post 6252313)
If this code is put into a script and run from a cron job, it does work. Running this same script from the prompt still means that there is a connection with a terminal. My guess is that it will also work with sendmail, unless the user is directly interacting with the program.

Code:

if [ -t 0 ];then echo Interactive; fi

Yes, it comes back "not interactive" when run from cron or sendmail. It comes back "interactive" when run inside a script at the command line if stdin is the keyboard. BUT, as I've said, it comes back "not interactive" if piping into the script.

There are some environment variable differences. the piped-script-command-line has LS_COLORS set. Cron does not. TERM is "linux" for cron and "xterm" for piped/command. cron has CONSOLE set; piped/command does not. There are other, none of which seem satisfactory.

I'll experiment more with test -t 2.

shruggy 05-20-2021 09:36 AM

You can try all of them and see whichever works:
Code:

#!/bin/sh
for i in 0:stdin 1:stdout 2:stderr
do [ -t ${i%:*} ] && echo ${i#*:} is terminal
done

Code:

$ ./inter
stdin is terminal
stdout is terminal
stderr is terminal
$
echo|./inter
stdout is terminal
stderr is terminal
$
./inter|cat
stdin is terminal
stderr is terminal
$
echo|./inter|cat
stderr is terminal


mfoley 05-20-2021 11:46 AM

I think the -t 2 solution will work for me. I'm going to mark that as the solution. I had no idea this would be so difficult!


All times are GMT -5. The time now is 03:52 AM.