Discussion:
[Help-bash] Unexpected behaviour in job control inside subshell environment
Diego Augusto Molina
2016-12-01 15:55:04 UTC
Permalink
Hi, I was looking for a way to test if the "sleep" command could be
coerced in some way to tell if the given arguments would be ok. The
thing is I want to test if an argument received by a function is of a
suitable format for that command. Different "sleep" implementations
accept different forms of arguments so I wanted to make sleep command
tell me if the argument is ok. So I came up with the following idea:

SLEEP_ARG=wrong-value;
(
sleep "10${SLEEP_ARG}" &
sleep 0.1; # Optional but recommended
jobs -r 1;
if [ $? -eq 0 ]; then
kill -9 $!;
exit 0;
else
exit 1;
fi;
) &> /dev/null;

The hypothesis is that any acceptable argument for "sleep" that
represents the time to sleep could have prepended any other number to
make sure it's a big enough time to test if it's running in background
or if it has exited immediately.
So, if the argument is right then it will sleep in background and "jobs
-r 1" will exit successfully, which would cause the first branch of the
"if" statement to kill the background process and exit the subshell
successfully. If the argument is wrong then "sleep" will exit
immediately and "jobs -r 1" will exit unsuccessfully, causing the second
branch of the "if" statement to take control and exit the subshell
unsuccessfully. The optional but recommended "sleep 0.1" ensures the
first "sleep" command has the time to exit unsuccessfully before bash
delivers control to the "jobs -r 1" statement after forking the first
"sleep" to background. The downside is that I'm already assuming that
"sleep" can receive decimal values.
Note that all stdout and stderr of the subshell will be lost completely
since all I need is to know if the argument is correct or not.

But the effect is odd: the subshell always returns successfully and I
can verify that "jobs -r 1" detects the first "sleep" as running even
when it isn't. Proof of concept:

SLEEP_ARG=wrong-value;
(
sleep "10${SLEEP_ARG}";
sleep 3; # Obscenely high
jobs -r 1;
echo "Exit status: $?";
);

Whether or not all this is of any use (or just overthinking utterly
simple things) is not as important as the fact that (I guess) it should
work. I'm probably settling for somthing simpler like assuming that the
argument should conform to some regexp.

Cheers.
Greg Wooledge
2016-12-01 16:29:34 UTC
Permalink
Post by Diego Augusto Molina
SLEEP_ARG=wrong-value;
(
sleep "10${SLEEP_ARG}" &
sleep 0.1; # Optional but recommended
jobs -r 1;
Remember that job control is disabled in scripts (non-interactive shells)
by default.

As far as wrapping the sleep(1) command to validate arguments, I would
go with something more like this:

ver=$(sleep --version 2>&1)
if [[ $ver = *GNU* ]]; then
: fractions are allowed
else
: fractions are probably going to blow up
fi

Actually *calling* sleep(1) with wacky arguments to see how long it
takes to die is pretty suboptimal, I should think.
Chris F.A. Johnson
2016-12-01 23:42:01 UTC
Permalink
Post by Greg Wooledge
Post by Diego Augusto Molina
SLEEP_ARG=wrong-value;
(
sleep "10${SLEEP_ARG}" &
sleep 0.1; # Optional but recommended
jobs -r 1;
Remember that job control is disabled in scripts (non-interactive shells)
by default.
As far as wrapping the sleep(1) command to validate arguments, I would
ver=$(sleep --version 2>&1)
$ ver=$(sleep --version 2>&1)
$ echo "$ver"
sleep: usage: sleep seconds[.fraction]
--
Chris F.A. Johnson, <http://cfajohnson.com>
Diego Augusto Molina
2016-12-02 12:07:26 UTC
Permalink
Post by Greg Wooledge
Post by Diego Augusto Molina
SLEEP_ARG=wrong-value;
(
sleep "10${SLEEP_ARG}" &
sleep 0.1; # Optional but recommended
jobs -r 1;
Remember that job control is disabled in scripts (non-interactive shells)
by default.
Great point, but I'm using an interactive shell.
Post by Greg Wooledge
As far as wrapping the sleep(1) command to validate arguments, I would
ver=$(sleep --version 2>&1)
if [[ $ver = *GNU* ]]; then
: fractions are allowed
else
: fractions are probably going to blow up
fi
Actually *calling* sleep(1) with wacky arguments to see how long it
takes to die is pretty suboptimal, I should think.
The reason of the original mail was to understand why the code didn't
work as expected. It wasn't intended to ask for a solution to the
problem of validating the argument given to the "sleep" program. The
real solution I came up to is:

SLEEP_TIME=something;
sleep_time_regex='^(\.[0-9]+)?$';
if
[ -z "$SLEEP_TIME" ] || # SLEEP_TIME must be a value
( # If we don't have the GNU implem, only integers are accepted
[[ "$(sleep --version 2>&1;)" != *GNU* ]] &&
[ -n "${SLEEP_TIME##*([0-9])}" ]
) || # If we do have the GNU implem, then we may have decimal point
[[ ! "${SLEEP_TIME##*([0-9])}" =~ ${sleep_time_regex} ]];
then
echo "Invalid SLEEP_TIME value.";
fi;

Note that I use a separate variable for the regex since the code won't
run on a (very) old Bash implementation I'm testing with.

Loading...