LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Slackware (https://www.linuxquestions.org/questions/slackware-14/)
-   -   Script Not Inheriting Environment Variables (https://www.linuxquestions.org/questions/slackware-14/script-not-inheriting-environment-variables-773608/)

Woodsman 12-05-2009 01:19 AM

Script Not Inheriting Environment Variables
 
I have a script that normally runs from a scheduler (at, cron). For testing I run the script manually from a terminal window.

The script is declared as #!/bin/sh.

When I run the script manually from the command line, the script does not inherit the $PS1 or $- environment variables.

When I change the declaration to #!/bin/bash then the script inherits the $PS1 variable but not the $-.

The script is rather lengthy and when I run from a terminal I want certain echo messages to appear to help me with troubleshooting and improving the script. I wanted to key off the $PS1 or $- variables to determine whether the script is interactive or non-interactive. Yet this plan does not seem to work.

I can manually run the script as interactive by prefixing sh -i before the script name. I then see my special test messages.

The script is not interactive in the sense that I need to respond to anything from the script. The script is interactive only in the sense that I want to see my special messages when run manually from the command line and I don't want to see these message when run from the schedulers.

Why does the script have to be declared as /bin/bash to see $PS1 and what can I do to inherit that variable and the $- variable? The variables are present in the terminal window. I thought child processes are supposed to inherit the environment variables of the parent process.

Thanks again.

catkin 12-05-2009 02:14 AM

Quote:

Originally Posted by Woodsman (Post 3780573)
When I run the script manually from the command line, the script does not inherit the $PS1 or $- environment variables.

Are they environment variables? Use the env command, when running the script both as sh and bash, to find out.
Quote:

Originally Posted by Woodsman (Post 3780573)
I wanted to key off the $PS1 or $- variables to determine whether the script is interactive or non-interactive. Yet this plan does not seem to work.

Testing PS1 is the standard test. As it does not work you could examine the output from the tty command to determine if there is a controlling terminal.

tuxdev 12-05-2009 02:17 AM

Scripts will not inherit variables that are not export'd

Woodsman 12-05-2009 10:39 AM

Quote:

Are they environment variables? Use the env command, when running the script both as sh and bash, to find out.
Yes, they are. The variable $PS1 is defined in /etc/profile and redefined in /etc/bashrc, which I source in ~/.bashrc. Within a terminal I can type echo $PS1 and see the variable contents. I also can type echo $- and see the contents. But those two variables are not passed to the script when I run the script manually from the command line.

Quote:

As it does not work you could examine the output from the tty command to determine if there is a controlling terminal.
Would you be more specific what you have in mind?

Quote:

Scripts will not inherit variables that are not export'd
I understand the statement but why are only those variables not being inherited? I temporarily inserted the set command in the script to list all environment variables. The environment variable list is the same as when I run the set command directly from the terminal except $PS1 is not listed.

I can get $PS1 in the script's environment only if I change the script declaration to /bin/bash rather than /bin/sh. Why does the declaration of /bin/sh ignore the $PS1 variable and /bin/bash does not? The cause would seem to be with the shell interpreter?

Note: On my systems /bin/sh is sym linked to /bin/bash.

voyciz 12-05-2009 10:52 AM

EDIT: nevermind, i'm an idiot

tuxdev 12-05-2009 11:34 AM

"set" prints both your environment variables and your shell variables, "printenv" only prints the environment variables.

Actually, for your purposes, it might be more appropriate to key off of whether or no stdout is a terminal, by using [ -t 1 ] or [[ -t 1 ]]

catkin 12-05-2009 11:34 AM

Quote:

Originally Posted by Woodsman (Post 3780951)
Yes, they are. The variable $PS1 is defined in /etc/profile and redefined in /etc/bashrc, which I source in ~/.bashrc. Within a terminal I can type echo $PS1 and see the variable contents. I also can type echo $- and see the contents.

Those tests do not show that they are environment variables; running the env command would and it would show a different set of environment variables depending on whether bash had been invoked as bash or sh (see below for specifics).
Quote:

Originally Posted by Woodsman (Post 3780951)
Would you be more specific what you have in mind?

Try running the tty command both when running the script from a command prompt and running the script from cron (in the cron case the output must be redirected to a file so you can examine it later).
Quote:

Originally Posted by Woodsman (Post 3780951)
I understand the statement but why are only those variables not being inherited? I temporarily inserted the set command in the script to list all environment variables. The environment variable list is the same as when I run the set command directly from the terminal except $PS1 is not listed.

The set command lists all variables, not only environment variables.
Quote:

Originally Posted by Woodsman (Post 3780951)
I can get $PS1 in the script's environment only if I change the script declaration to /bin/bash rather than /bin/sh. Why does the declaration of /bin/sh ignore the $PS1 variable and /bin/bash does not? The cause would seem to be with the shell interpreter?

Note: On my systems /bin/sh is sym linked to /bin/bash.

Bash behaves differently when run as bash and when run as sh. It uses different startup files and, when run as sh, switches to POSIX mode after reading the startup files. See this LQ post for informative links.

Woodsman 12-05-2009 12:18 PM

Thanks for the responses. :D

Seems the detection of $PS1 is controlled by the shell interpreter.

The /etc/file/magic configuration file plays a role. The shell interpreter uses that file to determine the shell's respective behavior. In that file the !/bin/sh declaration is interpreted as a Bourne shell. The !/bin/bash declaration is interpreted as a Bourne-Again shell.

The Bourne-Again shell will detect the $PS1 environment variable and the Bourne shell will not.

From the Advanced Bash-Scripting Guide, Chapter 2:

The sha-bang ( #!) [1] at the head of a script tells your system that this file is a set of commands to be fed to the command interpreter indicated. The #! is actually a two-byte [2] magic number, a special marker that designates a file type, or in this case an executable shell script (type man magic for more details on this fascinating topic). Immediately following the sha-bang is a path name. This is the path to the program that interprets the commands in the script, whether it be a shell, a programming language, or a utility. This command interpreter then executes the commands in the script, starting at the top (the line following the sha-bang line), and ignoring comments. [3]

From the Bash Reference Manual, 6.2 Bash Startup Files:

Since a shell invoked as sh does not attempt to read and execute commands from any other startup files . . .

This would indicate that despite the sym link between /bin/sh and /bin/bash, when invoked using the /bin/sh declaration, bash will not read and execute the bash startup files. That includes /etc/profile, etc/bashrc, ~/.bashrc, etc. Hence the $PS1 environment variable is unavailable when a script is invoked with !/bin/sh.

From the Advanced Bash-Scripting Guide,, Chapter 31:

Using Bash-specific functionality in a Bourne shell script (#!/bin/sh) on a non-Linux machine may cause unexpected behavior. A Linux system usually aliases sh to bash, but this does not necessarily hold true for a generic UNIX machine.

A shell script headed by #!/bin/sh will not run in full Bash-compatibility mode. Some Bash-specific functions might be disabled. Scripts that need complete access to all the Bash-specific extensions should start with #!/bin/bash.


The detection of the $PS1 variable is a Bourne-Again shell feature and is unavailable in the generic Bourne shell.

From the Advanced Bash-Scripting Guide,, Chapter 33-10:

Note that /bin/sh is a link to /bin/bash in Linux and certain other flavors of UNIX, and a script invoked this way disables extended Bash functionality.

Perhaps then the question is not how to detect $PS1, which is a Bourne-Again shell feature, but how to detect or invoke interactive mode in a portable manner across both the Bourne and Bourne-Again shells, or any shell. One trick for better portability would seem to be to use the /usr/bin/env declaration, but that trick basically is the same as using the !/bin/bash declaration.

The !/bin/sh declaration ensures better portability (POSIX compliance) between operating systems. Hence my use of the !/bin/sh declaration in my shell scripts. Which is why originally I tried to detect the contents of $- rather than $PS1. I must have run across a lot of this information long ago and that got lost in the cobwebs of my mind. :) So why doesn't $- work correctly?

Once upon a time I read that the $- variable detection is supposed to work across various shells and be more portable. Apparently that information is incorrect. I'd like to know when the $- detection method works within a script. My script always detects the variable but never sees the variable as confirming interactive mode.

When I start my script prefixed with the sh -i command, then the $- variable confirms interactive mode.

For the immediate issue I can change my script declaration to !/bin/bash and obtain the dual effects (interactive versus non-interactive) I described in my original post. Yet I would like a more portable method for toggling between interactive and non-interactive mode when running a script directly from the command line.

I suspect many people do not know about these subtle differences if they developed the habit of always using one declaration or the other.

Feel free to add information to help better clarify these subtle quirks.

Woodsman 12-05-2009 12:43 PM

Quote:

For the immediate issue I can change my script declaration to !/bin/bash and obtain the dual effects (interactive versus non-interactive) I described in my original post.
Huh. Changing the script declaration to !/bin/bash works great when I run the script directly from the command line but not when run from a scheduler. When run from a scheduler (at daemon), the $PS1 variable is available and although I don't want to see the special messages, they nonetheless appear in the output (mail from the at daemon).

There must be a better way to display certain messages when run directly at the command line and to ignore those messages when run in the background from a scheduler. Any ideas? :scratch:

Note: Hmm. Perhaps the $TERM variable will do the trick. When in a console the variable is set to linux and when in a terminal window the variable is set to xterm. When the script is run from the at daemon the variable is not part of the environment. The script declaration has no effect on the variable.

Caveat: The at daemon assigns a value of dumb to the $TERM variable. The variable will not appear in the script's environment variable list, and a null value therefore cannot be used as a test. My script test now looks for a $TERM value of linux or xterm to determine whether I am running the script directly from the command line.

voyciz 12-05-2009 03:27 PM

Interesting about that /etc/file/magic. You know it's not just a file, at least not here on Slack 13, it's a directory with tons of files.

Alien Bob 12-05-2009 03:40 PM

Quote:

Originally Posted by catkin (Post 3780986)
Try running the tty command both when running the script from a command prompt and running the script from cron (in the cron case the output must be redirected to a file so you can examine it later).

As catkin said, the "tty" command can be used to determine if your job is attached to a terminal or not. Try running this bit of shell script on the commandline, and then schedule it in at or cron. Watch the different outputs.

Code:

#!/bin/sh
if $(tty -s) ; then
  echo "Runs in a terminal"
else
  echo "Does NOT run in a terminal"
fi

Eric

Woodsman 12-05-2009 05:32 PM

Quote:

Try running this bit of shell script on the commandline, and then schedule it in at or cron. Watch the different outputs.
Thanks for the example.

I tested the snippet. Works as advertised. :)

Although operating differently, seems this approach and the method I tested in my previous post with the $TERM variable produce the same desired result with respect to me wanting to run scripts in either console/terminal or with the at daemon.

Thanks everybody for an interesting discussion! :D

rustek 01-07-2010 04:35 PM

It's an interesting discussion but I would rather see the diagnostic output as run from cron than from a terminal.

Scripts can run correctly in the terminal and not run correctly from cron.

I have seen many times that the format of the output of a command used in a script can be different if run in the term or in cron.

It's easy to suppress standard out while still having standard error mailed to you from a cron job, just end your crontab entry with > /dev/null

Suppressing your output from the script itself when it is running in cron seems like extra work to do something that could be counterproductive and can easily be done with 12 characters in your crontab entry.

Or so it seems to me.

Russ


All times are GMT -5. The time now is 11:37 PM.