A mental note and short reminder on how to use the “test” command in bash if-loops or using the && and || operators for conditional scripting.
This is a short introduction - and personal reminder - on how to use the test command.
The Google “Shell Style Guide”↗ prefers double [[ … ]] over the older (but POSIX portable) single [ … ] or literal test and I won’t question this. 1
The test command returns 0 (True) or 1 (False), which we can use conditionally in if-loops or using the && and || operators, and one could also simply examine the return value by displaying $? e.g.
$ [[ "foo" != "bar" ]]; echo $?
0
$ [[ "foo" == "bar" ]]; echo $?
1
$ [[ "foo" == "foo" ]]; echo $?
0
$ [[ "foo" != "foo" ]]; echo $?
1
$ [[ 5 > 2 ]]; echo $?
0There are tons of applications, but we will mostly use it to grep for certain outputs or check variables or for the existence of files.
Consult help test for further details.
var=baz # this is our test input, i.e. [[ "foo" == $var ]] will return "False" (i.e. "1"): [[ "foo" == $var ]]; echo $? vs. [[ "baz" == $var ]]; echo $?
if [[ "foo" == $var ]]; then
echo "VAR equals foo"
elif [[ "bar" == $var ]]; then
echo "VAR equals bar"
else
echo "we got it all wrong and VAR equals something else"
fiThe use of echo here is just a substitute for any other command that might be used here.
There is a more cunning, and cryptic or 31337, way of writing if-statements using the && and || operators.
&& and || are actually LOGICAL operators, i.e.
&& = AND
|| = OR
the general logic for scripting or chaining commands would be
"FOO ; BAR" # Run FOO and then BAR, regardless of success of FOO
"FOO && BAR" # Run BAR if FOO succeeded, i.e. returns "True"
"FOO || BAR" # Run BAR if FOO failed, i.e. returns "False"
"FOO &" # Run FOO in the background.
"FOO | BAR" # pipe the output of FOO into BAR for further processingto substitute an if-else-loop with && and || use as follows
# the nifty way
[[ "foo" != "bar" ]] && echo "true, foo != bar" || echo "if false, obviously VAR must have been foo then as well"
# which is the same as
if [[ "foo" != "bar" ]]; then
echo "true, foo != bar"
else
echo "if false, obviously VAR must have been foo then as well"
fithese operators can also be used to chain multiple tests/ conditions together into one test e.g.
$ var=baz
# OR; is one or the other "True"
$ [[ "foo" == $var || "baz" == $var ]] && echo "true" || echo "false"
true
# AND; both must be "True"
$ [[ "foo" == $var && "baz" == $var ]] && echo "true" || echo "false"
falseFor example, when I’m downloading, extracting and forwarding files based on a list of ISBNs and do not know whether I’ll find a package for each of the given ISBNs, then I need to know the failed ISBN, so that I do not include it within my extra metadata file, in which I only want to provide data for succeeded ISBNs. Obviously, I’m aware that I could include a test at an earlier stage already, but this is my workshop;-)
ISBNLIST=ISBNs4NYU.txt
BASEDIR=$(pwd)
WORKDIR=$BASEDIR/ProQuest_Deposit_$(date +%F); mkdir $WORKDIR; cp $ISBNLIST $WORKDIR; cd $WORKDIR
# Download WebPDFs directly out of the PDW
for ISBN in $(cat $ISBNLIST); do
echo $ISBN
# getting "documentId"
DOCUMENTID=$(curl -s --proxy "http://proxy-URL:1337" https://product-data-warehouse-URL/publication/$ISBN | grep '"id"' | head -n1 | grep -oP '(?<="id" : ").+?(?=")')
# downloading ZIP
curl -s --proxy "http://proxy-URL:1337" https://product-data-warehouse-URL/document/$DOCUMENTID/zip -o $ISBN.zip
# unzip WebPDF only
unzip -q -j $ISBN.zip *$DOCUMENTID.pdf
# rename to ISBN.pdf
mv $DOCUMENTID.pdf $ISBN.pdf
# if WebPDF exists, then upload to target FTP
if [[ -f $ISBN.pdf ]]; then
curl -T $ISBN.pdf -u UN:PW ftp://ftp.data-recipient.com/upload/
echo "$ISBN" >> isbn_list_4_onix.txt
else echo "$ISBN" >> WebPDF_missing.txt
fi
# clean-up
rm $ISBN.zip $ISBN.pdf
done
cd $BASEDIRI usually only need -f, but again check help test for other useful options like
File operators:
-d FILE True if file is a directory.
-e FILE True if file exists [regardless of type, e.g. file, directory, device etc.].
-f FILE True if file exists and is a regular file [and not a directory or device etc.].
-s FILE True if file exists and is not empty.
A -nt B Test if file A is newer than file B, by modification date.
A -ot B Test if file A is older than file B.
This check could also be negated, so that the outcome is “True” when the file does NOT exist:
if [[ ! -f $ISBN.pdf ]]; then
echo "$ISBN.pdf does not exist."
fiFor example, I want to prompt the user to specify a file, like an ISBN list, when there was no command line argument (i.e. $1) given in the first place:
# check for input variable, if empty prompt for file in current directory, else move on with the given input variable
if [[ -z "$1" ]]; then
echo; echo "Please select ISBN list: (choose number)"; echo
select L in *; do test -n "$L" && break; echo ">>> Invalid Selection"; done
else
L=$1
fiString operators:
-z STRING True if string is empty.
-n STRING True if string is NOT empty.
# nifty alternative
[[ -z "$var" ]] && echo "true, VAR is empty" || echo "false, VAR is Not empty"This IBM tutorial on Bash test and comparison functions↗ is also very helpful.
to be continued …
[[ … ]] reduces errors as no pathname expansion or word splitting takes place between [[ and ]]. In addition, [[ … ]] allows for regular expression matching, while [ … ] does not. ↩︎
For attribution, please cite this work as
Schmalfuß (2017, Nov. 9). OS DataMercs: bash 'test' command. Retrieved from https://www.datamercs.net/posts/2017-11-09-bash-test-command/
BibTeX citation
@misc{schmalfuß2017bash,
author = {Schmalfuß, Olaf},
title = {OS DataMercs: bash 'test' command},
url = {https://www.datamercs.net/posts/2017-11-09-bash-test-command/},
year = {2017}
}