LinuxQuestions.org
Visit Jeremy's Blog.
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Distributions > Slackware
User Name
Password
Slackware This Forum is for the discussion of Slackware Linux.

Notices


Reply
  Search this Thread
Old 12-05-2009, 01:19 AM   #1
Woodsman
Senior Member
 
Registered: Oct 2005
Distribution: Slackware 14.1
Posts: 3,482

Rep: Reputation: 546Reputation: 546Reputation: 546Reputation: 546Reputation: 546Reputation: 546
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.
 
Old 12-05-2009, 02:14 AM   #2
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
Blog Entries: 31

Rep: Reputation: 1198Reputation: 1198Reputation: 1198Reputation: 1198Reputation: 1198Reputation: 1198Reputation: 1198Reputation: 1198Reputation: 1198
Quote:
Originally Posted by Woodsman View Post
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 View Post
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.
 
Old 12-05-2009, 02:17 AM   #3
tuxdev
Senior Member
 
Registered: Jul 2005
Distribution: Slackware
Posts: 2,012

Rep: Reputation: 115Reputation: 115
Scripts will not inherit variables that are not export'd
 
Old 12-05-2009, 10:39 AM   #4
Woodsman
Senior Member
 
Registered: Oct 2005
Distribution: Slackware 14.1
Posts: 3,482

Original Poster
Rep: Reputation: 546Reputation: 546Reputation: 546Reputation: 546Reputation: 546Reputation: 546
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.

Last edited by Woodsman; 12-05-2009 at 10:57 AM.
 
Old 12-05-2009, 10:52 AM   #5
voyciz
Member
 
Registered: Mar 2004
Distribution: Slackware
Posts: 425

Rep: Reputation: 40
EDIT: nevermind, i'm an idiot
 
Old 12-05-2009, 11:34 AM   #6
tuxdev
Senior Member
 
Registered: Jul 2005
Distribution: Slackware
Posts: 2,012

Rep: Reputation: 115Reputation: 115
"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 ]]
 
Old 12-05-2009, 11:34 AM   #7
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
Blog Entries: 31

Rep: Reputation: 1198Reputation: 1198Reputation: 1198Reputation: 1198Reputation: 1198Reputation: 1198Reputation: 1198Reputation: 1198Reputation: 1198
Quote:
Originally Posted by Woodsman View Post
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 View Post
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 View Post
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 View Post
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.
 
Old 12-05-2009, 12:18 PM   #8
Woodsman
Senior Member
 
Registered: Oct 2005
Distribution: Slackware 14.1
Posts: 3,482

Original Poster
Rep: Reputation: 546Reputation: 546Reputation: 546Reputation: 546Reputation: 546Reputation: 546
Thanks for the responses.

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.
 
Old 12-05-2009, 12:43 PM   #9
Woodsman
Senior Member
 
Registered: Oct 2005
Distribution: Slackware 14.1
Posts: 3,482

Original Poster
Rep: Reputation: 546Reputation: 546Reputation: 546Reputation: 546Reputation: 546Reputation: 546
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?

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.

Last edited by Woodsman; 12-05-2009 at 01:43 PM.
 
Old 12-05-2009, 03:27 PM   #10
voyciz
Member
 
Registered: Mar 2004
Distribution: Slackware
Posts: 425

Rep: Reputation: 40
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.
 
Old 12-05-2009, 03:40 PM   #11
Alien Bob
Slackware Contributor
 
Registered: Sep 2005
Location: Eindhoven, The Netherlands
Distribution: Slackware
Posts: 8,559

Rep: Reputation: 8098Reputation: 8098Reputation: 8098Reputation: 8098Reputation: 8098Reputation: 8098Reputation: 8098Reputation: 8098Reputation: 8098Reputation: 8098Reputation: 8098
Quote:
Originally Posted by catkin View Post
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
 
Old 12-05-2009, 05:32 PM   #12
Woodsman
Senior Member
 
Registered: Oct 2005
Distribution: Slackware 14.1
Posts: 3,482

Original Poster
Rep: Reputation: 546Reputation: 546Reputation: 546Reputation: 546Reputation: 546Reputation: 546
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!
 
Old 01-07-2010, 04:35 PM   #13
rustek
Member
 
Registered: Jan 2010
Location: Melbourne, IA, USA
Distribution: Ubuntu
Posts: 93

Rep: Reputation: 8
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
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
sudo perl script with environment variables powah Programming 1 04-22-2009 11:22 AM
Bash script environment variables mbjunior99 SUSE / openSUSE 4 12-28-2005 12:40 AM
Need help setting environment variables via shell script srosburg Linux - Newbie 2 12-08-2005 07:58 PM
Setting environment variables from shell script theta Linux - General 5 09-02-2004 08:50 PM
Setting environment variables from a script... sylvain_gnu Linux - Newbie 5 04-20-2004 12:31 PM

LinuxQuestions.org > Forums > Linux Forums > Linux - Distributions > Slackware

All times are GMT -5. The time now is 07:06 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration