1

I had created the below script which does the comparison between hh:mm:ss format variables.

#!/bin/sh

lag=00:00:00
MAX_LAG=00:05:00

echo $lag
echo $MAX_LAG

if [ "$lag" \< "$MAX_LAG" ]
##if [ "$lag" -lt "$MAX_LAG" ]
##if [ "$lag" -lt "$MAX_LAG" ]
then
   echo "No LAG found."
else
   echo "LAG more than $MAX_LAG was found."
fi

The script is giving me proper output when run in linux box

[test@sandboxtest02 ~]$ ./testhh.sh
00:00:00
00:05:00
No LAG found.

But when I run the same script in solaris box, this script is giving me the error,

% sh test.sh
00:00:00
00:05:00
test.sh: test: unknown operator <

Any suggestions what needs to be changed in solaris box?

Rui F Ribeiro
  • 56,709
  • 26
  • 150
  • 232
SinghVK
  • 113

5 Answers5

3

< isn't a standard operator for test/[..] (see quote below). It seems supported by Bash, Dash, busybox and others, but apparently not by Zsh (I'm not sure about different versions of Ksh). Bash, ksh and Zsh would also support [[ str1 < str2 ]] (without need to escape the <). You'll have to find and use a shell that supports one of those.

(Note that Solaris 10 and earlier versions even have a pre-POSIX /bin/sh, not that it matters here since it's not standard anyway.)

Some additional primaries newly invented or from the KornShell appeared in an early proposal as part of the conditional command ([[]]): s1 > s2, s1 < s2, ... They were not carried forward into the test utility when the conditional command was removed from the shell because they have not been included in the test utility built into historical implementations of the sh utility.

ilkkachu
  • 138,973
2

There are a few adjustments to make it work in Solaris-sh; here's my approach:

#!/bin/sh

lag=00:06:00
MAX_LAG=00:05:00

IFS=:
set -f

set -- $lag
lagtotal=`expr 3600 * $1 + 60 * $2 + $3`

set -- $MAX_LAG
maxlag=`expr 3600 * $1 + 60 * $2 + $3`

if [ "$lagtotal" -lt "$maxlag" ]
then
  echo "No LAG found."
else
  echo "LAG more than $MAX_LAG was found."
fi

(note that the strictly less-than comparison disagrees slightly with your wording of "LAG more than ...")

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
2

Any suggestions what needs to be changed in Solaris box?

You can fix your script with a very minor change. Just modify the shebang, i.e replace:

#!/bin/sh

by:

#!/bin/bash

and that's it. You are using a bashism so just stay with bash, that's leave less risk for other subtle incompatibilities that may arise in other part of the script; bash has been available in standard installations for decades under Solaris.

You are running Solaris 10 which is quite old (first release in 2005), and with it /bin/sh is an ancient shell (sticking to the original Bourne shell syntax) that, by design, no to break compatibility with old scripts, hasn't changed since 1989 (30 years!).

The current release of Solaris (Solaris 11) is using a POSIX shell as /bin/sh but it's still not bash so you would need the same fix.

jlliagre
  • 61,204
  • if [ "$a" \< "$b" ]; then ... will not work even with /bin/ksh, /usr/xpg4/bin/sh or the ksh93 from solaris 11. ksh93 does support the < and > operators, but only with [[, not with [ or test. –  Feb 09 '19 at 05:15
  • @pizdelect Doh! You are right. It was "working" with the OP sample test code though ;-) – jlliagre Feb 09 '19 at 09:10
2

In the POSIX toolchest, expr and awk can be used to compare strings.

Both use strcoll() for the comparison (same as sort: the locale's collation order), but you can set the locale to C to get a strcmp() type of comparison. For things like HH:MM:SS it doesn't matter anyway.

if expr "$time1" '<' "$time2" > /dev/null; then
  printf '%s\n' "$time1 was before $time2"
fi

Now, again it doesn't matter for HH:MM:SS, but in the general case, note that expr will do a numeric comparison if the operands are decimal integer numbers and do string comparison otherwise. That means that for instance 2 will be seen as less than 10 even though strcmp() or strcoll() would say otherwise.

Also, expr will fail if the operands are expr operators:

$ x=+ y=-; expr "$x" '<' "$y"
expr: syntax error

You can work around both problems by prefixing the operands with something that prevents them from being taken as numbers or operators:

expr "x $x" '<' "x $y"

With awk, you have a similar problem. You'll need:

awk 'BEGIN{exit(!(""ARGV[1] < ""ARGV[2]))}' "$x" "$y"

To make sure the comparison is a string comparison and not a number comparison.

You can use helper functions like:

compare() {
  expr "x $1" "$2" "x $3"
}

or:

compare() {
  awk 'BEGIN{exit(!(""ARGV[1] '"$2"' ""ARGV[2]))}' "$1" "$3"
}

And use as:

if compare "$time1" '<' "$time2"; then
  printf '%s\n' "$time1 was before $time2"
fi

In any case, as always, on Solaris, you need to make sure /usr/xpg4/bin is before /bin and /usr/bin in $PATH otherwise you get antiquated non-standard utilities. In particular, /bin/awk on Solaris won't work with the above code. It's the one from the 70s without ARGV.

For that same reason, you don't want to use #!/bin/sh in your she-bang. The standard/POSIX sh on Solaris is /usr/xpg4/bin/sh (though in Solaris 11, /bin/sh changed from the Bourne shell to ksh93 which makes it a lot more POSIX compliant; also note that /usr/xpg4/bin/sh has many conformance bugs).

1

On recent solaris systems[1], /bin/sh is ksh93, so the following should do:

if [[ "$lag" < "$MAX_LAG" ]]; then ..

Notice the double brackets [[ .. ]]; when used inside [[ .. ]], the < operator will do a lexicographical comparison in ksh, which should do for those HH:MM:SS-like strings.

If your shell really doesn't support that, you may bring out the big guns:

trim(){ ifs=IFS; IFS=:; v=$1; set -- $2; eval "$v=$1$2$3"; IFS=$ifs; }
trim a "$lag"
trim b "$MAX_LAG"
if [ "$a" -lt "$b" ]; then ..

[1] /bin/sh on solaris is ksh93 since solaris 11.

  • /bin/sh doesn't support the bash extended test ([[) – jesse_b Feb 08 '19 at 16:38
  • on my solaris box, it does. /bin/sh is a kind of ksh on recent (less than 10 years old?) solaris systems. –  Feb 08 '19 at 16:57
  • @Jesse_b in fact, it's more like 8 years: "Shell changes - The default shell, /bin/sh, is now linked to ksh93". Sorry for the imprecision. –  Feb 08 '19 at 17:26
  • It will also work in any system where /bin/sh is /bin/bash but I wouldn't recommend using non posix tests with a posix hashbang. – jesse_b Feb 08 '19 at 17:37
  • the question was specifically about solaris, and I also gave a kludge that should work with a POSIX shell (and I'll fix it to work with the pre-POSIX bourne shell, too) –  Feb 08 '19 at 17:48
  • also notice that /bin/sh is a POSIX shell only in a hypothetical world; on most (all?) systems /bin/sh is either something pre-posix or something ksh-like -- as I just get tripped by trying to set IFS to the nul string with "$*" (which must've worked according to the standard) –  Feb 08 '19 at 18:04
  • On most if not all current systems, /bin/sh is a POSIX shell (mostly one of bash, dash, ksh93 and perhaps still some ksh88). Solaris 10 was the last Unix to provide a pre-POSIX shell there. Note also the the POSIX shell standard was designed using ksh as model, so the OSes providing ksh or ksh like shell are fine. – jlliagre Feb 08 '19 at 23:38
  • No /bin/sh is 100% POSIX compliant, especially in things like the builtin behavior, and most of them share a lot of extensions, so there's no system where you could use /bin/sh to validate scripts for POSIX compliance. But apart the profoundly religious significance of believing that /bin/sh is POSIX while it isn't, could you guys point out what's wrong with my answer? The 1st example was specifically mentioning ksh from the start, and the 2nd will work in any POSIX shell and many pre-POSIX shells. Why is better to fork external commands like expr or awk? –  Feb 09 '19 at 04:51
  • the 2nd trick should work in any bourne shell, even the original one, if you don't use a function and repeat it instead ifs=$IFS; set -- $lag; lag="$1$2$3"; ... ; IFS=$ifs. –  Feb 09 '19 at 05:04