Link to home
Start Free TrialLog in
Avatar of Bernard Savonet
Bernard SavonetFlag for France

asked on

Problem with the vanishing value of a variable inside a bash script

I'm consistently failing on the following:
- I have a bash script, say example.sh, a simplified content of which is listed below
- it is called with 0 or 1 argument, the normal value of which is "nomail" when I don't want a mail to be sent in the process
- the script first checks if nomail was provided as an argument and if yes gives the same value to the internal variable NOMAIL (otherwise empty). In the version below, it exports the value, as one of my (failed) attemps to solve the issue
- then runs the script somescript as user postgres (thru su)
- then launches another script mainscript with as argument the content of the NOMAIL variable

What happens:
- when running the example script from command line, everything works as expected, and the call to mainscript does indeed include the non-empty content of NOMAIL as as an argument
- but when running from cron, the call to mainscript is done with an empty value for NOMAIL!!!

(my first attempts were made without export, so I tried that just in despair!)

#

LE_SCRIPT="/root/somescript.sh"


if  [ x$1 == 'xnomail' ] || [ x$2 == 'xnomail' ] ; then
      export NOMAIL='nomail'
fi

su postgres -c "$LE_SCRIPT"


exec /root/mainscript.sh $NOMAIL

Open in new window

Avatar of ozo
ozo
Flag of United States of America image

How is it called from cron?
Avatar of Bernard Savonet

ASKER

In the crontab:
57 6 * * * /root/example.sh

57 7-23 * * * /root/example.sh nomail
ASKER CERTIFIED SOLUTION
Avatar of noci
noci

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
At first glance, looks like you haven't passed the -p (preserve environment) su flag.

Without this flag, as I recall, the environment inherits from /etc/bashrc + /etc/profile* which means you must run su -p to see $NOMAIL in your scripts.

At least this is how I recall su working.

Refer to your specific su man page to ensure you correctly pass all your environment variables through su.
Thx all
Your questions and suggestions finally led me to the solution
Stupid me!!!

My main script was missing the #!/bin/bash and thus the cron was probably ran as sh instead of bash

This solved the issue
Glad you found a solution so fast!
Good to see you got a solution, Bernard, but here are some side notes which may be useful in your scripting, in case you didn't already know them.

Instead of this:
    if [ x$1 == 'xnomail' ] || [ x$2 == 'xnomail' ] ; then
which I think is a messy workaround to handle missing arguments, you could make it shorter and more readable like this:
    if [ "$1" == 'nomail' || "$2" == 'nomail' ]; then
or this:
    if [ "$1" == 'nomail' -o "$2" == 'nomail' ]; then    # Might be required for sh?
And since it's bash, I think you could clean it up more like this:
    if [[ $1 == 'nomail' || $2 == 'nomail' ]]; then
But it seems strange to me that 'nomail' would be passed as argument 1 or 2.  Is it meant to be that way?  If it would always be argument 1 you simplify it to this:
    if [[ $1 == 'nomail' ]]; then

tel2
Thx tel2

Your suggestions about tests combinations are heloful and I will probably use one oe the other in ly next scripts

As for the double argument test: this script can be called with 0 to 2 arguments in any order, hence the double test
 In case one argument is not there, using x$1 always has a non empty value
OK Bernard.

And if you want it in a bash one-liner, you could do it like this:
    [[ $1 == 'nomail' || $2 == 'nomail' ]] && export NOMAIL='nomail'

Do you understand that all of my suggested options handle empty values (AKA missing arguments)?
Thx
- the oneliners are great and I would have used them initially if I knew how much instructions were in the « then part »
- I had used a formulation like yours, but if $2 is empty then bash explodes at interpreting
... || == ´nomail’ ]]
While my trick would prevent explosion since we would then have
 || x== ´xnomail’ ]]
I'm glad I asked that last question, Bernard.

I am well aware of how your "x" prefix works.  That is what I described in my first post as "a messy workaround to handle missing arguments".  Did you read that?

I tested my solutions in bash and they work even with missing arguments, as per the last sentence of my previous post.
They do that by either:
    a) Using "quotes" around the $1 & $2
OR:
    b) The use of bash's [[ & ]].

Have you tested my solutions, Bernard?  What results did you get?
Avatar of noci
noci

[ != [[  
[ is a command theshell runs.
[[ ... ]] are bash constructs.  like  $((  )) can be used for expressions.
Thx tel2 for the suggestions in your post on 2019-10-16

1/   if [ "$1" == 'nomail' || "$2" == 'nomail' ]; then
explodes in bash

2/   if [ "$1" == 'nomail' -o "$2" == 'nomail' ]; then  
works in bash but explodes in case of an empty arg

3/ if [[ $1 == 'nomail' || $2 == 'nomail' ]]; then
works and handles correctly an empty arg

Of course this works if "nomail" is expected to be in the 2 first args. If there can be an undefined number of args, then I could explore the array of args with a loop  using "$#" and "$@"

THe final script:
#!/bin/bash

NOMAIL='unchanged'

if [[ "$1" == nomail  ||  "$2" == nomail ]] ; then
   NOMAIL='nomail'
fi

echo "NOMAIL: [$NOMAIL]"

Open in new window

Hi Bernard,

Sorry for the delayed response - just noticed your post now.

1/   if [ "$1" == 'nomail' || "$2" == 'nomail' ]; then
explodes in bash
Good point - my mistake, sorry.  I should have had:
    if [ "$1" == 'nomail' ] || [ "$2" == 'nomail' ]; then

2/   if [ "$1" == 'nomail' -o "$2" == 'nomail' ]; then

works in bash but explodes in case of an empty arg
If that's the case, then why does it work fine regardless of arguments, when I run it in Cloud Linux and OpenBSD (UNIX)?  Check this out:
$ cat test2.sh
#!/bin/bash
if [ "$1" == 'nomail' -o "$2" == 'nomail' ]; then
        echo 'True'
else
        echo 'False'
fi

$ ./test2.sh
False
$ ./test2.sh mail
False
$ ./test2.sh nomail
True
$ ./test2.sh mail nomail
True

3/ if [[ $1 == 'nomail' || $2 == 'nomail' ]]; then
works and handles correctly an empty arg
Glad to see you finally agree with me about that, but now that you know that, why are you using the quotes around $1 & $2 in the final bash script you posted above, i.e.:
    if [[ "$1" == nomail  ||  "$2" == nomail ]] ; then

Note, if you're going to remove quotes around literals like 'nomail' in tests like the one above, as you have, and you want to be consistent, then you might like to also remove them from assignments like:
    NOMAIL='unchanged'
    NOMAIL='nomail'
which could be reduced to:
    NOMAIL=unchanged
    NOMAIL=nomail
But as you probably know, you'd need quotes around literals which contain spaces, etc.