Question

I have a bash script called test.sh which, for the sake of simplicity, prints one line to stdout and one line to stderr.

test.sh:

#!/bin/bash
echo "this is to stdout"
echo "this is to stderr" 1>&2

I want to run the script test.sh at 7:00 PM, but only if certain conditions are met. To this end, I have another bash script called schedule.sh, which checks some stuff and then submits the command to at to be run later.

I want the output of test.sh (both stdout and stderr) to be sent to me in an email. I use mailx to do this so I can get a nice subject name.

Furthermore, I want at to shut up. No output from at because it always sends me ugly emails (no subject line) if at produces any output.

schedule.sh:

#!/bin/bash
my_email="me@example.com" # Email is a variable
# Check some stuff, exit if certain conditions not met
echo "~/test.sh 2>&1 | mailx -s\"Cool title\" $my_email" | at 7:00 PM &> /dev/null

What's interesting is that when I run schedule.sh from cron (which runs the script with sh), it works perfectly. However, when I manually run schedule.sh from the terminal (NB: I'm using tcsh), at (not mailx) sends me an email saying

Ambiguous output redirect.

I'm not sure why the shell I run schedule.sh from makes a difference, when schedule.sh is a bash script.

Here is my thinking in looking at schedule.sh. Everything within the quotation marks "~/test.sh 2>&1 | mailx -s\"Cool title\" me@email.com" should be an argument to at, and at runs that argument as a command using sh. The redirection 2>&1 | is in the style of sh for this reason.

When I remove 2>&1 and only pipe the stdout of test.sh to mailx, it does work; however, I receive 2 emails: one with stdout from mailx and another from stderr from at.

What gives? How can I make this work regardless of the shell I'm calling it from?

Thanks.

edit:

uname -o says my OS is GNU/Linux

Here is uname -a if it helps:

Linux [hostname censored] 2.6.9-89.ELlargesmp #1 SMP Mon Jun 22 12:46:58 EDT 2009 x86_64 x86_64 x86_64 GNU/Linux

When I check the at contents using at -c, here's what I see:

#!/bin/sh
# atrun uid=xxxxx gid=xxxxx
# mail  username 0
# ...
SHELL=/bin/tcsh; export SHELL
# ...
${SHELL:-/bin/sh} << `(dd if=/dev/urandom count=200 bs=1 2>/dev/null|LC_ALL=C tr -d -c '[:alnum:]')`

~/test.sh 2>&1 | mailx -s"Cool title" me@example.com

I'm having a hard time understanding the second to last line... is this going to execute using $SHELL or /bin/sh?

Was it helpful?

Solution

The command executed via at is:

~/test.sh 2>&1 | mailx -s\"Cool title\" $my_email

The behavior of at command varies from one system to another. On Linux, the command is executed using /bin/sh. In fact, on my system (Linux Mint 14), it prints a warning message:

$ echo 'printenv > at.env' | at 19:24
warning: commands will be executed using /bin/sh

On Solaris, the command is executed by the shell specified by the current value of the $SHELL environment variable. Using an account where my default shell is /bin/tcsh on Solaris 9, I get:

% echo 'printenv > at.env' | at 19:25
commands will be executed using /bin/tcsh
job 1397874300.a at Fri Apr 18 19:25:00 2014
% echo 'printenv > at.env' | env SHELL=/bin/sh at 19:28
commands will be executed using /bin/sh
job 1397874480.a at Fri Apr 18 19:28:00 2014

Given that at's behavior is inconsistent (and frankly confusing), I suggest having it execute just a single command, with any I/O redirection being performed inside that command. That's the best way to ensure that the command will be executed correctly regardless of which shell is used to execute it.

For example (untested code follows):

echo '#!/bin/bash' > tmp.bash
echo "~/test.sh 2>&1 | mailx -s\"Cool title\" $my_email" >> tmp.bash
chmod +x tmp.bash
echo "./tmp.bash" | at 7:00 PM
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top