Bash - case statement always enters default - bash

I am writing a Bash script to parse a CSV file (values separated by ; character) and extract some arguments. Depending on the current argument read from the file a specific string is appended to a variable. However, the case statement always enters the default state *) and I can't figure out why.
I have no problems reading the .csv file. Already did that and displayed the output of the arguments read from the file. Everything works perfectly. The problem is that the case statement is not processed as expected.
So, the problem is not reading the .csv file but the processing of the arguments in the case statement.
This is my code:
while IFS=";" read -r arg
do
case ${arg} in
"valueX")
var+="blabla"
;;
"valueY")
var+="blublu"
;;
*)
echo -e "Argument ${arg} not supported"
exit 1
;;
esac
done < filename
Assuming "valueX" is the current argument read from the file. Somehow the script always outputs:
Argument "valueX" not supported
Apparently, the argument (here: "valueX") read from the file is correct, but the script won't enter the corresponding state. Instead, it always enters the default state, no matter what value ${arg} holds.
[EDIT]
I thought it would be a good idea to ask the question more generally, but it turns out to be confusing. So here is the full bash script and .csv file:
Script:
#!/bin/bash
# style reset
STYLE_RESET='\e[0m'
# Red foreground color
FOREGROUND_RED='\e[31m'
# Green foreground color
FOREGROUND_GREEN='\e[32m'
# Blue foreground color
FOREGROUND_BLUE='\e[34m'
# Red background color
BACKGROUND_RED='\e[41m'
# Green background color
BACKGROUND_GREEN='\e[42m'
# Blue background color
BACKGROUND_BLUE='\e[44m'
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
usage()
{
echo "ToDo"
exit 1
}
# --------------------------------------- #
# --- Checking Command Line Arguments --- #
# --------------------------------------- #
# Supported command line arguments:
# -h|--help
# -a|--address IP of SSH server for remote VMAF
# -u|--user User for SSH server login
# -d|--doe DoE worksheet exported as CSV UTF-8 file
PARAMS=""
while (( "$#" )); do
case "$1" in
-h|--help)
usage
shift
;;
-u|--user)
if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then # Check length of argument and first character of argument != '-'
SSH_USER=$2
shift 2
else
echo "Error: Argument for $1 is missing" >&2
exit 1
fi
;;
-a|--address)
if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then # Check length of argument and first character of argument != '-'
SSH_IP=$2
shift 2
else
echo "Error: Argument for $1 is missing" >&2
exit 1
fi
;;
-d|--doe)
if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then # Check length of argument and first character of argument != '-'
DOE_FILE=$2
shift 2
else
echo "Error: Argument for $1 is missing" >&2
exit 1
fi
;;
-*|--*=) # unsupported flags
echo "Error: Unsupported flag $1" >&2
exit 1
;;
*) # DEFAULT
PARAMS="${PARAMS} $1" # preserve positional arguments
shift
;;
esac
done
# set positional arguments in their proper place
eval set -- "${PARAMS}"
# ---------------------- #
# --- Processing DoE --- #
# ---------------------- #
echo -e "${BACKGROUND_BLUE}Processing DoE specified in file ${DOE_FILE}:${STYLE_RESET}"
echo -e "${BACKGROUND_BLUE}Configuring Video source for GStreamer pipeline...${STYLE_RESET}"
GSTPIPE_SRC="gst-launch-1.0 -e "
run=1
while IFS=";" read -r motion bitrate_in bitrate_out twopass iframe quantI quantP quantB mvbuffer cabac vbv
do
echo -e "\n\n${BACKGROUND_BLUE}Setting #${run}:${STYLE_RESET}"
echo -e "${BACKGROUND_BLUE}${motion} ${bitrate_in} ${bitrate_out} ${twopass} ${iframe} ${quantI} ${quantP} ${quantB} ${mvbuffer} ${cabac} ${vbv}${STYLE_RESET}"
echo -e "\n${BACKGROUND_BLUE}Generating GStreamer pipelines...${STYLE_RESET}"
case ${motion} in
"low")
GSTPIPE_SRC+="videotestsrc pattern=colors num-buffers=300 ! " # -> no motion content
case ${bitrate_in} in # -> bitrate of video source (width*height*framerate)
"low") # -> 640x480
GSTPIPE_SRC+="'video/x-raw, width=(int)640, height=(int)480, framerate=(fraction)30/1, format=(string)I420' "
width=640
height=480
fps=30
;;
"high") # -> 3840x2160
GSTPIPE_SRC+="'video/x-raw, width=(int)3840, height=(int)2160, framerate=(fraction)30/1, format=(string)I420' "
width=3840
height=2160
fps=30
;;
*)
echo -e "\n\n${BACKGROUND_RED}Input bitrate ${bitrate_in} not supported${STYLE_RESET}"
echo -e "Use low, or high instead"
exit 1
;;
esac
;;
"high")
GSTPIPE_SRC+="filesrc location=${SCRIPT_DIR}/extensive.mp4 " # -> high motion content
case ${bitrate_in} in # -> bitrate of video source (width*height*framerate)
"low") # -> 640x480
GSTPIPE_SRC+="blocksize=460800 ! " # blocksize=width*height*bytesPerPixel (I420->12bit->bytesPerPixel=1.5)
GSTPIPE_SRC+="'video/x-raw, width=(int)640, height=(int)480, framerate=(fraction)30/1, format=(string)I420' "
width=640
height=480
fps=30
;;
"high") # -> 3840x2160
GSTPIPE_SRC+="blocksize=12441600 ! " # blocksize=width*height*bytesPerPixel (I420->12bit->bytesPerPixel=1.5)
GSTPIPE_SRC+="'video/x-raw, width=(int)3840, height=(int)2160, framerate=(fraction)30/1, format=(string)I420' "
width=3840
height=2160
fps=30
;;
*)
echo -e "\n\n${BACKGROUND_RED}Input bitrate ${bitrate_in} not supported${STYLE_RESET}"
echo -e "Use low, or high instead"
exit 1
;;
esac
;;
*)
echo -e "${BACKGROUND_RED}Argument ${motion} for DoE factor 'motion' not supported${STYLE_RESET}"
echo -e "Use low, or high instead"
exit 1
;;
esac
GSTPIPE_ENC=$GSTPIPE_SRC
GSTPIPE_REF=$GSTPIPE_SRC
GSTPIPE_REF+="! y4menc ! filesink location=${SCRIPT_DIR}/reference${fps}fps_run${run}.y4m"
GSTPIPE_ENC+="! nvvidconv ! 'video/x-raw(memory:NVMM)' ! nvv4l2h264enc "
GSTPIPE_ENC+="bitrate=${bitrate_out} EnableTwopassCBR=${twopass} "
case ${iframe} in
"low")
GSTPIPE_ENC+="iframeinterval=20 SliceIntraRefreshInterval=10 "
;;
"high")
GSTPIPE_ENC+="iframeinterval=120 SliceIntraRefreshInterval=60 "
;;
*)
echo -e "${BACKGROUND_RED}Argument ${motion} for DoE factor iframe is not supported${STYLE_RESET}"
echo -e "Use low, or high instead"
exit 1
;;
esac
# The range of B frames does not take effect if the number of B frames is 0. (https://docs.nvidia.com/jetson/l4t/index.html#page/Tegra%20Linux%20Driver%20Package%20Development%20Guide/accelerated_gstreamer.html#wwpID0E0YX0HA)
GSTPIPE_ENC+="quant-i-frames=${quantI} quant-p-frames=${quantP} quant-b-frames=${quantB} num-b-frames=1 EnableMVBufferMeta=${mvbuffer} cabac-entropy-coding=${cabac} "
GSTPIPE_ENC+="! nvv4l2decoder ! nvvidconv ! 'video/x-raw' ! y4menc ! filesink location=${SCRIPT_DIR}/distorted${fps}fps_run${run}.y4m"
echo -e "${BACKGROUND_BLUE}Distorted Video:${STYLE_RESET}"
echo -e "${FOREGROUND_BLUE}${GSTPIPE_ENC}${STYLE_RESET}"
echo -e "${BACKGROUND_BLUE}Reference Video:${STYLE_RESET}"
echo -e "${FOREGROUND_BLUE}${GSTPIPE_REF}${STYLE_RESET}"
# --- Launching GStreamer pipelines (surpress detailed output) --- #
echo -e "${BACKGROUND_BLUE}Launching GStreamer pipeline for encoded video (distorted):${STYLE_RESET}"
eval "${GSTPIPE_ENC[#]}" #> /dev/null
echo -e "${BACKGROUND_BLUE}Launching GStreamer pipeline for uncompressed video (reference):${STYLE_RESET}"
eval "${GSTPIPE_REF[#]}" #> /dev/null
# --- Create and Check Remote Directories --- #
echo -e "\n${BACKGROUND_BLUE}Video transfer to remote machine:${STYLE_RESET}"
SSH_DIR_ADD="v4l2h264/motion_${motion}/bitrate_${bitrate_in}/"
SSH_DIR="/home/${SSH_USER}/metrics/${SSH_DIR_ADD}" # Create variable holding path of directory for both:
# 1.) reference.y4m and distorted.y4m videos
# 2.) remote VMAF
ssh ${SSH_USER}#${SSH_IP} "test -d ${SSH_DIR}" < /dev/null # see https://stackoverflow.com/questions/9393038/ssh-breaks-out-of-while-loop-in-bash
if [ $? -ne 0 ]; then # Directory does not exist
echo -e "${BACKGROUND_BLUE}Creating remote directory for run #${run}: ${SSH_DIR}...${STYLE_RESET}"
ssh ${SSH_USER}#${SSH_IP} "mkdir -p ${SSH_DIR}" < /dev/null # see https://stackoverflow.com/questions/9393038/ssh-breaks-out-of-while-loop-in-bash
else # Directory already exists
echo -e "${BACKGROUND_BLUE}Remote directory ${SSH_DIR} already exists${STYLE_RESET}"
fi
# --- Transfer Video Files --- #
echo -e "${BACKGROUND_BLUE}Transfering reference and distorted videos of run #${run}:${STYLE_RESET}"
scp ${SCRIPT_DIR}/distorted${fps}fps_run${run}.y4m ${SSH_USER}#${SSH_IP}:${SSH_DIR} < /dev/null # see https://stackoverflow.com/questions/9393038/ssh-breaks-out-of-while-loop-in-bash
scp ${SCRIPT_DIR}/reference${fps}fps_run${run}.y4m ${SSH_USER}#${SSH_IP}:${SSH_DIR} < /dev/null # see https://stackoverflow.com/questions/9393038/ssh-breaks-out-of-while-loop-in-bash
# --- Run VMAF on Remote Machine --- #
echo -e "\n${BACKGROUND_BLUE}Running VMAF metric for DoE run #${run} on remote machine...${STYLE_RESET}"
ssh ${SSH_USER}#${SSH_IP} "vmaf -r ${SSH_DIR}/reference${fps}fps_run${run}.y4m -d ${SSH_DIR}/distorted${fps}fps_run${run}.y4m -w ${width} -h ${height} -p 420 -b 12 -o ${SSH_DIR}/log${fps}fps_run${run}.xml --threads 8" < /dev/null # see https://stackoverflow.com/questions/9393038/ssh-breaks-out-of-while-loop-in-bash
echo -e "${BACKGROUND_BLUE}VMAF metric for DoE run #${run} finished.${STYLE_RESET}"
# --- Remove Videos on Remote Machine (Laptop) --- #
echo -e "\n${BACKGROUND_BLUE}Removing videos from remote machine${STYLE_RESET}"
ssh ${SSH_USER}#${SSH_IP} "rm ${SSH_DIR}/distorted${fps}fps_run${run}.y4m ${SSH_DIR}/reference${fps}fps_run${run}.y4m" < /dev/null # see https://stackoverflow.com/questions/9393038/ssh-breaks-out-of-while-loop-in-bash
# --- Remove videos on local machine (SPU) --- #
echo -e "\n${BACKGROUND_BLUE}Removing videos from local machine${STYLE_RESET}"
rm distorted${fps}fps_run${run}.y4m reference${fps}fps_run${run}.y4m
((run++))
done < <(cut -d ";" -f5,6,7,8,9,10,11,12,13,14,15,16 ${DOE_FILE} | tail -n +2) # read from the second line of the file (no header) and only read the columns specified with -f
((run--))
exit 0
.csv file:
"StdOrder";"RunOrder";"CenterPt";"Blocks";"motion";"bitrate_in";"bitrate_out";"twopass";"iframe";"quantI";"quantP";"quantB";"mvbuffer";"cabac"
1;1;1;1;"low";"low";200000;"false";"low";0;0;0;"true";"true"
6;2;1;1;"high";"low";80000000;"false";"low";51;0;51;"true";"false"
8;3;1;1;"high";"high";80000000;"false";"high";0;0;0;"false";"true"
2;4;1;1;"high";"low";200000;"false";"high";0;51;51;"false";"false"
3;5;1;1;"low";"high";200000;"false";"high";51;0;51;"false";"false"
7;6;1;1;"low";"high";80000000;"false";"low";0;51;51;"true";"false"
10;7;1;1;"high";"low";200000;"true";"high";51;0;0;"true";"false"
9;8;1;1;"low";"low";200000;"true";"low";51;51;51;"false";"true"
4;9;1;1;"high";"high";200000;"false";"low";51;51;0;"true";"true"
13;10;1;1;"low";"low";80000000;"true";"high";0;0;51;"true";"true"
5;11;1;1;"low";"low";80000000;"false";"high";51;51;0;"false";"true"
12;12;1;1;"high";"high";200000;"true";"low";0;0;51;"false";"true"
15;13;1;1;"low";"high";80000000;"true";"low";51;0;0;"false";"false"
14;14;1;1;"high";"low";80000000;"true";"low";0;51;0;"false";"false"
16;15;1;1;"high";"high";80000000;"true";"high";51;51;51;"true";"true"
11;16;1;1;"low";"high";200000;"true";"high";0;51;0;"true";"false"

Apparently, the argument (here: "valueX") read from the file is correct
Since you consider the quotes being part of the argument to be correct, in order to match them with a case pattern, you have to escape the pattern quotes:
case ${arg} in
\"valueX\")
Interestingly, this appears to be a Bash (documentation) error, as that says only:
Each pattern undergoes tilde expansion, parameter expansion, command substitution, and arithmetic expansion.
It doesn't tell that the pattern undergoes quote removal.

None of your sample data contains valueX, but assuming you were looking for e.g. "high" in the fifth field, you need to ask the shell to split into at least six fields;
while IFS=";" read -r _first _second _third _fourth arg _rest
do
case ${arg} in
'"high"')
var+="blabla"
;;
'"low"')
var+="blublu"
;;
*)
echo -e "Argument ${arg} not supported"
exit 1
;;
esac
done < filename
Probably a better solution here is to use Awk, though.
awk -F ';' '$5 !~ /"(high|low)"/ { print "Argument " $5 " is not supported" }' filename
Without the rest of your script, it's hard to tell in which direction to continue this; very often, if you are using Awk anyway, it makes sense to implement the rest of the logic in Awk, too.

Related

How to execute ksh script from bash

I currently have a ksh script which invokes another ksh script. The "parent" ksh script needs to be invoked from a bash shell in the context of the ksh shell user. Trying the following throws back this error message
As user root in the bash shell
su - whics -c '/usr/bin/nohup /whics/t99/wv.4gm/wv99b.4gs/wv99b.sh -s 1 -m u -sleep 5 > ./nohup.out &'
/whics/t99/wv.4gm/wv99b.4gs/wv99b.sh[8]: .: wh_setENV.sh: cannot open [No such file or directory]
wh_setENV.sh is actually in /whics/t99/bin
However, when running the below commands in order I do not get this error
server:~ su - whics
server:/whics/t99 cd ./wv.4gm/wv99b.4gs
server:/whics/t99/wv.4gm/wv99b.4gs nohup ./wv99b.sh -s 1 -m u -sleep 5 &
server:/whics/t99/wv.4gm/wv99b.4gs nohup: ignoring input and appending output to `/home/whics/nohup.out'
[1] + Done nohup ./wv99b.sh -s 1 -m u -sleep 5 &
server:/whics/t99/wv.4gm/wv99b.4gs cat /home/whics/nohup.out Mon Sep 17 12:27:40 AEST 2018 : Start wv99b
wv99b.sh
#!/bin/ksh
# Copyright (C) 1992-1997 Wacher Pty. Limited
# Sccsid: %Z% %M%%Y% %Q%%I% %E%
myname=${0##*/} # a useful identifying variable
mydir=${0%$myname} # where this script is
vSFX=${myname##*.}
. wh_setENV.sh # P4813 - when using 4js:WebServices, the $fglidir/lib in LD_LIBRARY_PATH causes problems
test $debugxv && set -xv
#--------------------------------------------------------------------------------------------------------------------------------------#
wv99b_msg() {
vERR="`date` : ${vMSG}"
echo $vERR | tee -a ${vLOG}
}
#--------------------------------------------------------------------------------------------------------------------------------------#
wv99b_sysFragments() {
vSYSFRAGOK="0"
vSYSFRAGMENTS="${vTABNAME}.sysfrags.unl" ; rm -f $vSYSFRAGMENTS
$WH_ISQL $company - <<! 2>/dev/null | sed "/exprtext/d;/^$/d;s/ //g;s/[()]//g" |cut -f1 -d'=' >| ${vSYSFRAGMENTS}
select F.exprtext
from systables S, sysfragments F
where S.tabid > 99
and S.tabtype = "T"
and S.tabname = "${vTABNAME}"
and S.tabid = F.tabid
and S.tabtype = F.fragtype
and F.evalpos = 0
;
!
if [ -s ${vSYSFRAGMENTS} ] ; then
# search for the vCOLUMN in the vSYSFRAGMENTS output
vSYSFRAGOK=`grep -i ${vKEY} ${vSYSFRAGMENTS} 2>/dev/null | wc -l | awk '{print $1}'`
else
vSYSFRAGOK="0"
rm -f ${vSYSFRAGMENTS} # cleanup
fi
}
# MAIN #
vARGS="$#"
vHERE=`pwd`
vLOG="${vHERE}/errlog"
vD=0 # debug indicator
vI=0 # infile indicator
vQ=0 # email indicator
vM=0 # mode indicator
vS=0 # serial indicator
vNO_MULTI=0 # default to false
vNO_PROGI=0 # default to false
vTABLE=0 # default to 0
vSLEEP=5 # default to 0
for i in $vARGS
do
case "$i" in
-debug) vD=$2 ;;
-infile) vI=$2 ;;
-table) vTABLE=$2 ;;
-sleep) vSLEEP=$2 ;;
-no_multi) vNO_MULTI=$2 ;;
-no_progi) vNO_PROGI=$2 ;;
-m) vM=$2 ;;
-q) vQ=$2 ;;
-s) vS=$2 ;;
esac
shift
done
[[ ${vS} -eq 0 ]] && vMSG="-s parameter not supplied" && wv99b_msg && exit 1
vHERE=`pwd`
if [ ${vD} -eq 1 ] ; then
vDEBUG=" -debug 1"
else
vDEBUG=""
fi
if [ ${vI} -eq 0 ] ; then
vINFILE="wv99b.in"
else
vINFILE="${vI}"
fi
# INIT
vWVI="wv99b_I" # the name of the (I)dentify script
vWVIS="${vWVI}_${vS}" # the name of the (I)dentify script PLUS SERIAL
vWVIO="${vWVIS}.unl" # the name of the (I)dentify script
rm -f ${vWVIO}
# Check that transaction-logging is off
# check that vINFILE exists
if [ ! -s "${vINFILE}" ] ; then
vMSG="Error cannot read input file $vINFILE" ; wv99b_msg ; exit 1
fi
# Process only one(1) table
if [ ${vTABLE} != "0" ] ; then
vTABLE_FILTER=" -table ${vTABLE} "
else
vTABLE_FILTER=""
fi
# We need to check if we are running client/server
#
vDB=`echo $company | awk 'BEGIN {FS="#" } { print $1 }'`
vDBSRV=`echo $company | awk 'BEGIN {FS="#" } { print $2 }'`
case X${vDBSRV}X in
XX) vREMOTE_DB="" ;;
*) vREMOTE_DB=" -db ${vDB} -dbsrv ${vDBSRV} " ;;
esac
#_end
vMSG="Start wv99b" ; wv99b_msg
So in the wv99b.sh file, I changed
. wh_setENV.sh
to
. /whics/t99/bin/wh_setENV.sh
However, now I get the error
cannot read input file wv99b.in
I checked wv99b.in and it is in the same directory as 'wv99b.sh' (i.e. /whics/t99/wv.4gm/wv99b.4gs/ )
wh_setENV.sh
#!/usr/bin/ksh
test $debugxv && set -xv
trap door 1 2 3 5 9 15
#---------------------------------------------------------------------#
door() {
echo "`date` ERROR($?) occured in $0" >> $WH/batch.4gm/trap.log
} #end door
#---------------------------------------------------------------------#
# Script to set Environment variables for various scripts
# Stef
# Unix specific
umask 002
: ${WH:="/whics/prod"}
set -a
TERM=xterm
vHERE=`pwd`
TERMCAP=$WH/etc/termcap
vHOST=`hostname | cut -f1 -d'.'`
set +a
#LD_LIBRARY_PATH="$WH/lib.4gm/S_lib:$fglibdir/S_lib" # GUC R481
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$INFORMIXDIR/lib/c++:$INFORMIXDIR/lib/cli:$INFORMIXDIR/lib/client:$INFORMIXDIR/lib/csm:$INFORMIXDIR/lib/dmi"
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$INFORMIXDIR/lib:$INFORMIXDIR/lib/esql:$INFORMIXDIR/lib/tools.$CCODE"
export LD_LIBRARY_PATH
# EOF #
UPDATE: After OP modified/updated the question ...
wv99b.in is referenced in wv99b.sh
you don't provide the path for wv99b.in so ...
if you invoke wv99b.sh from some directory other than /whics/t99/wv.4gm/wv99b.4gs ...
when you run your test [ ! -s "${vINFILE}" ] your script (correctly) determines that "${vINFILE}" is not located in the directory where you invoked wv99b.sh from
Net result ... the original problem with sh_setENV.sh has been fixed but now the same exact problem is occurring for wv99b.in, with the same solution needed here ... invoke wv99b.sh from its home directory or provide the full path to wv99b.in.
ORIGINAL POST:
Expanding on Andre's comment ...
In wv99b.sh you have the following line:
. wh_setENV.sh
This is going to look for wh_setENV.sh in the directory where wv99b.sh is invoked from.
In your original example you've provided the full path to wv99b.sh but the question is ... what directory is that call invoked from? We can tell from the error ...
wh_setENV.sh: cannot open [No such file or directory]
... that wv99b.sh was not invoked from /whics/t99/wv.4gm/wv99b.4gs otherwise it would have found wh_setENV.sh.
From your second example it appears that the full path to wh_setENV.sh is: /whics/t99/wv.4gm/wv99b.4gs/wh_setENV.sh so you have a couple options:
in your initial example make sure you cd /whics/t99/wv.4gm/wv99b.4gs before executing wv99b.4gs
or
update wv99b.4gs to include the full path to the location of sh_setENV.sh, eg:
. /whics/t99/wv.4gm/wv99b.4gs/wh_setENV.sh

bash getopts, why is my script not parsing additional input parameter?

I'm trying to create a bash script which reads a set of optional input parsing parameters. I tried to follow this example on GitHub.com.
I tried to add another integer parameter to read, which I called x (or xxx-size). I thought it was sufficient to duplicate the stack-size line command in the while loop, but it was not.
When I run the script, the system puts the x parameter as the last one in the order, and does not read it. I cannot understand what is going on.
Here's my code:
#!/bin/bash
#
# Example of how to parse short/long options with 'getopt'
#
OPTS=`getopt -o vhnxs: --long verbose,dry-run,help,xxx-size,stack-size: -n 'parse-options' -- "$#"`
if [ $? != 0 ] ; then echo "Failed parsing options." >&2 ; exit 1 ; fi
echo "$OPTS"
eval set -- "$OPTS"
VERBOSE=false
HELP=false
DRY_RUN=false
STACK_SIZE=0
XXX=-1
printf "\n1st parameter: "$1"\n"
printf "2nd parameter: "$2"\n"
printf "3rd parameter: "$3"\n"
printf "4th parameter: "$4"\n"
printf "5th parameter: "$5"\n\n"
while true; do
case "$1" in
-v | --verbose ) VERBOSE=true; shift ;;
-h | --help ) HELP=true; shift ;;
-n | --dry-run ) DRY_RUN=true; shift ;;
-x | --xxx-size ) XXX="$2"; shift; shift ;;
-s | --stack-size ) STACK_SIZE="$2"; shift; shift ;;
-- ) shift; break ;;
* ) break ;;
esac
done
echo VERBOSE=$VERBOSE
echo HELP=$HELP
echo DRY_RUN=$DRY_RUN
printf "STACK_SIZE "$STACK_SIZE"\n"
printf "XXX "$XXX"\n"
printf "\n\n"
Here's what happens if I try to set the s parameter to 100 and the x parameters to 65:
$ ./script.sh -s 100 -x 65
Standard output:
-s '100' -x -- '65'
1st parameter: -s
2nd parameter: 100
3rd parameter: -x
4th parameter: --
5th parameter: 65
VERBOSE=false
HELP=false
DRY_RUN=false
STACK_SIZE 100
XXX --
As you can see, the program does not associate the value 65 to XXX, as I would like. How can I solve this problem?
Thanks!

Execution sequence in shell script and passing the right arguments to functions

I have a shell script that I use to launch some ROS launcher file (not that important for my question). My requirement is to use some arguments and therefore I am using getopts.
Basically, I am getting one file or a few, playing them with ROS and simultaneously recording all the files back to a single file. After that, if I have provided an argument -r or -c, I would like to run two additional operations (reindex and compress) on the recorded file. But it is required that the other process are done, before I can run the -r and -c. I am using the wait keyword, but I am not sure I really understand the flow. In other words, the playing and recording should be done and only then -r and -c should be run if provided as arguments.
Second question is related to how do I get or pass the same file that was outputted to these two functions (reindex and compress)?
So my desired format is:
./myscript -i file1 -i file2 -o /home/output -r -c
#!/bin/bash
OUTPUT_FILE_NAME="output_$(date +%Y.%m.%d--%H_%M_%S)"
usage="
$(basename "$0") [-i] [-o] [-r] [-c] [-h] -- to be done"
# Reset is necessary if getopts was used previously in the script.
OPTIND=1
while getopts ":i:orch:" opt; do
case $opt in
i ) echo "INPUT file - argument = $OPTARG"; input_files+=($OPTARG) ;;
o ) echo "OUTPUT dir - argument = $OPTARG"; output_dir=($OPTARG) ;;
r ) echo "REINDEX"; reindex ;;
c ) echo "COMPRESS"; compress ;;
h ) echo "$usage"
graceful_exit ;;
* ) echo "$usage"
exit 1
esac
done
# Shift off the options
shift $((OPTIND-1))
roslaunch myLauncher.launch &
echo "Number of loaded files: ${#input_files[#]}"
echo -n "FILES are:"
rosbag play ${input_files[#]} &
rosbag record -o $output_dir/$OUTPUT_FILE_NAME -a &
wait
function reindex{
rosbag reindex $output_dir/$OUTPUT_FILE_NAME
}
function compress{
rosbag reindex $output_dir/$OUTPUT_FILE_NAME
}
Thank you in advance!
You're very close to where you need to be — and using getopts puts you firmly on the correct track, too. Note whether or not you need to reindex or compress in the option parsing loop. Then, after the music has been played and the output file written, run the code from the functions if you need to:
#!/bin/bash
OUTPUT_FILE_NAME="output_$(date +%Y.%m.%d--%H_%M_%S)"
usage="$(basename "$0") [-i file] [-o file] [-r] [-c] [-h]"
input_files=() # Empty list of files/tracks
output_dir="." # Default output directory
r_flag="no" # No reindex by default
c_flag="no" # No compress by default
while getopts ":i:orch:" opt; do
case $opt in
(i) echo "INPUT file - argument = $OPTARG"; input_files+=("$OPTARG");;
(o) echo "OUTPUT dir - argument = $OPTARG"; output_dir="$OPTARG";;
(r) echo "REINDEX"; r_flag="yes";;
(c) echo "COMPRESS"; c_flag="yes";;
(h) echo "$usage" >&2; exit 0;;
(*) echo "$usage" >&2; exit 1;;
esac
done
# Shift off the options
shift $((OPTIND-1))
if [ $# != 0 ] || [ "${#input_files[#]}" = 0 ] ||
then
echo "$usage" >&2
exit 1
fi
roslaunch myLauncher.launch &
echo "Number of loaded files: ${#input_files[#]}"
echo -n "FILES are:"
rosbag play "${input_files[#]}" &
rosbag record -o "$output_dir/$OUTPUT_FILE_NAME" -a &
wait
if [ "$r_flag" = "yes" ]
then
rosbag reindex "$output_dir/$OUTPUT_FILE_NAME"
fi
if [ "$c_flag" = "yes" ]
then
rosbag compress "$output_dir/$OUTPUT_FILE_NAME"
fi
I didn't keep the functions since they didn't provide any value in the rewritten code.

Dealing with multiple flags via getopt

I am using a script which should be invoked using only a single parameter to each flag as follows:
./testit.sh -n 123 -t tvar -b bvar -s svar my_program.exe flags to my program
where all flags are just flags to the script, and the script will launch my_program.exe with the flags flags to my program. The simple method being used to do this is getopts as follows:
#!/bin/bash
# contents of "test_it.sh"
echo "\$* = "$*
getopt_out=`getopt t:T:b:B:s:S:n:N $*`
echo "getopt_out = "$getopt_out
echo "\$* = "$*
set -- `getopt t:T:b:B:s:S:n:N $*`
echo "\$* = "$*
while [ $1 != -- ]; do
shift
done
shift
echo "**********************************************************************************"
echo "*** The program I want to run is: "$*
echo "**********************************************************************************"
where the (correct) output is:
**********************************************************************************
*** The program I want to run is: my_program.exe flags to my program
**********************************************************************************
I, however, need to send multiple numerical options to -n as follows:
./testit.sh -n 123 456 789 -t tvar -b bvar -s svar my_program.exe flags to my program
which gives incorrect output:
**********************************************************************************
*** The program I want to run is: 456 789 my_program.exe flags to my program
**********************************************************************************
How can I, from within 'testit.sh', get rid of those extra numbers? I am able to deal with them (log their values) at the beginning of the script so that they are no longer needed. Since the testit.sh script relies on shift, is there a way for me to completely disregard (throw out) the numeric values without messing up the flow of the commands, so that shift can still be used?
EDIT: Upon further investigation of my original script, I noticed that the output of getopts is different than the one posted in my minimal example. I have updated the minimal example along with the proposed workaround in an answer, though I would appreciated other (probably more canonical and/or correct) methods for dealing with this).
It turns out the original script which was causing me problems actually used POSIXLY_CORRECT=1 which slightly changes the order of -- in the output. I have posted the new code (including POSIXLY_CORRECT=1), along with my workaround for anyone who is interested:
#!/bin/sh
echo "\$* = "$*
POSIXLY_CORRECT=1
export POSIXLY_CORRECT
getopt_out=`getopt t:T:b:B:s:S:n:N: $*`
echo "getopt_out = "$getopt_out
set -- `getopt t:T:b:B:s:S:n:N: $*`
echo "\$* = "$*
double_dash_too_soon=false
while [ $1 != -- ]; do
echo "\$1 = "$1
case $1 in
-t)
# process this flag
shift
;;
-b)
# process this flag
shift
;;
-s)
# process this flag
shift
;;
-n)
This_num=$2
echo "This_num = "$This_num
shift
while [[ $2 != "-"[a-z,A-Z] ]] ; do
echo "Dealing with number!"
if [[ $1 == "--" ]]; then
echo "WARNING: '--' encountered during numeric reads!!" >&2
double_dash_too_soon=true
fi
shift
done
;;
esac
shift
if [ "$double_dash_too_soon" == true ]; then
double_dash_too_soon=false
echo "(1) \$* -> "$*
set -- `getopt t:T:b:B:s:S:n:N $*`
echo "(2) \$* -> "$*
fi
done
shift
echo "**********************************************************************************"
echo "*** The program I want to run is: "$*
echo "**********************************************************************************"

BASH getopt command returns its own parameters instead of command line parameters

I am creating a BASH script and working with the BASH getopt command to parse command line arguments. Instead of returning the arguments provided on the command line, getopt returns the arguments that were supplied to the getopt command. I am not sure what could be going on, as the code that I have was working perfect, and seemingly out of nowhere, it has stoped working correctly (and no, I haven't updated anything or changed any code or environment settings). I can't use getopts (with the extra 's') because it is, for some unknown reason, not installed on the machine that will be running this script.
Even though the script is supplied with zero command line arguments, the getopt command is for some reason returning all of the arguments that I have supplied, minus the -o flag, instead of the expected -- value indicating the end of the options. The code that I have is as follows:
SHORT_OPTS=":hvso:l:d:n:p:t:"
LONG_OPTS="help,version,submit-job,output:,library:,job-dir:"
LONG_OPTS="${LONG_OPTS},num-nodes:,num-procs:,max-time:"
OPTS=$(getopt -o "${SHORT_OPTS}" -l "${LONG_OPTS}" -a -n "${PROG_NAME}" -- "${#}")
# Check for invalid command line options and arguments
if [[ ${?} -ne ${SUCCESS} ]] ; then
echo -e "${PROG_NAME}: error: Invalid option or argument\n" >&2
usage ; exit ${FAILURE}
else
echo "BEFORE $#"
eval set -- ${OPTS}
echo "AFTER $#"
fi
# Process command line options and their arguments
while true ; do
case "${1}" in
-h | --help)
# Display script usage information and exit
usage ; exit ${SUCCESS} ;;
-v | --version)
# Display script version information and exit
echo "${PROG_NAME} v${PROG_VERSION}" ; exit ${SUCCESS} ;;
-s | --submit-job)
# Enable automatic submission of the Moab job
JOB_AUTO_SUBMIT="${PREF_YES}" ; shift 1 ;;
-o | --output)
# Set the base name for output file names
TARGET="${2}" ; shift 2 ;;
-l | --library)
# Set the library to use for NWChem atomic configurations
NW_LIB="${2}" ; shift 2 ;;
-d | --job-dir)
# Ensure the specified directory for the Moab job exists
if [[ -e "${2}" ]] ; then
JOB_WORK_DIR=$(resolvePath "${2}") ; shift 2
else
echo -e "${PROG_NAME}: error: -d ${2}: No such directory\n"
usage ; exit ${FAILURE}
fi ;;
-n | --num-nodes)
# Ensure the number of compute nodes is greater than zero
if positiveInt "${2}" ; then
JOB_NODES="${2}" ; shift 2
else
echo -n "${PROG_NAME}: error: -n ${1}: Number of "
echo -e "job nodes must be a positive integer\n"
usage ; exit ${FAILURE}
fi ;;
-p | --num-procs)
# Ensure the number of processors per node is greater than zero
if positiveInt "${2}" ; then
JOB_PROCS="${2}" ; shift 2
else
echo -n "${PROG_NAME}: error: -p ${2}: Number of "
echo -e "processors per node must be a positive integer\n"
usage ; exit ${FAILURE}
fi ;;
-t | --max-time)
# Ensure the maximum job runtime is in the correct format
if [[ "${2}" == [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ]] ; then
JOB_MAX_TIME="${2}" ; shift 2
else
echo -n "${PROG_NAME}: error: -t ${2}: Invalid time "
echo -e "format, please use hh:mm:ss format\n"
usage ; exit ${FAILURE}
fi ;;
--)
# No more options to process
shift ; break ;;
esac
done
# Check to see if POTCAR and CONTCAR locations were specified
if [[ ${#} -eq 2 ]] ; then
# Regular expressions for identifying POTCAR and CONTCAR files
PCAR_REGEX="[Pp][Oo][Tt][Cc][Aa][Rr]"
CCAR_REGEX="[Cc][Oo][Nn][Tt][Cc][Aa][Rr]"
# Attempt to identify POTCAR and CONTCAR argument ordering
if [[ ${1} =~ ${PCAR_REGEX} && ${2} =~ ${CCAR_REGEX} ]] ; then
POTCAR="${1}" ; CONTCAR="${2}" ; shift 2
else
POTCAR="${2}" ; CONTCAR="${1}" ; shift 2
fi
# Accept exactly two or zero command line arguments
elif [[ ${#} -ne 0 ]] ; then
echo "${PROG_NAME}: error: ${#}: Invalid argument count, expected [2|0]"
echo "$#"
exit ${FAILURE}
fi
Given this code, and running the application, I get the following output:
BEFORE
AFTER -- :hvso:l:d:n:p:t: -l help,version,submit-job,output:,library:,job-dir:,num-nodes:,num-procs:,max-time: -a -n vasp2nwchem --
vasp2nwchem: error: 7: Invalid argument count, expected [2|0]
:hvso:l:d:n:p:t: -l help,version,submit-job,output:,library:,job-dir:,num-nodes:,num-procs:,max-time: -a -n vasp2nwchem --
So, the code enters the while loop portion of the code, jumps to the last case, and shifts off the first --, leaving me with all of the arguments that I supplied to getopt, minus the -o flag.
Any light that anyone could shed on this conundrum would be immensely appreciated, because it is seriously about to send me over the edge, especially since this code was functional no less than thrity minutes ago, and has now stopped working entirely!!!
I don't see anything wrong. I have GNU getopt installed as /usr/gnu/bin/getopt (and BSD getopt in /usr/bin), so this script (chk.getopt.sh) is almost equivalent to the start of yours, though I do set the PROG_NAME variable. This is more or less the SSCCE (Short, Self-Contained, Complete Example) for your rather substantial script.
#!/bin/bash
PROG_NAME=$(basename $0 .sh)
SHORT_OPTS=":hvso:l:d:n:p:t:"
LONG_OPTS="help,version,submit-job,output:,library:,job-dir:"
LONG_OPTS="${LONG_OPTS},num-nodes:,num-procs:,max-time:"
OPTS=$(/usr/gnu/bin/getopt -o "${SHORT_OPTS}" -l "${LONG_OPTS}" -a -n "${PROG_NAME}" -- "$#")
# Check for invalid command line options and arguments
if [[ ${?} -ne ${SUCCESS} ]] ; then
echo -e "${PROG_NAME}: error: Invalid option or argument\n" >&2
usage ; exit ${FAILURE}
else
echo "BEFORE $#"
eval set -- ${OPTS}
echo "AFTER $#"
fi
When I run it, this is the output:
$ bash -x chk.getopt.sh -ooutput -nnumber -pperhaps -ppotato -- -o oliphaunt
++ basename chk.getopt.sh .sh
+ PROG_NAME=chk.getopt
+ SHORT_OPTS=:hvso:l:d:n:p:t:
+ LONG_OPTS=help,version,submit-job,output:,library:,job-dir:
+ LONG_OPTS=help,version,submit-job,output:,library:,job-dir:,num-nodes:,num-procs:,max-time:
++ /usr/gnu/bin/getopt -o :hvso:l:d:n:p:t: -l help,version,submit-job,output:,library:,job-dir:,num-nodes:,num-procs:,max-time: -a -n chk.getopt -- -ooutput -nnumber -pperhaps -ppotato -- -o oliphaunt
+ OPTS=' -o '\''output'\'' -n '\''number'\'' -p '\''perhaps'\'' -p '\''potato'\'' -- '\''-o'\'' '\''oliphaunt'\'''
+ [[ 0 -ne '' ]]
+ echo 'BEFORE -ooutput' -nnumber -pperhaps -ppotato -- -o oliphaunt
BEFORE -ooutput -nnumber -pperhaps -ppotato -- -o oliphaunt
+ eval set -- -o ''\''output'\''' -n ''\''number'\''' -p ''\''perhaps'\''' -p ''\''potato'\''' -- ''\''-o'\''' ''\''oliphaunt'\'''
++ set -- -o output -n number -p perhaps -p potato -- -o oliphaunt
+ echo 'AFTER -o' output -n number -p perhaps -p potato -- -o oliphaunt
AFTER -o output -n number -p perhaps -p potato -- -o oliphaunt
$
This all looks correct; the options before the double dash have been split from their arguments, and the ones after the double dash are OK.
So, it is not obvious that there's a problem with your code. Even with an empty string as the program name, it worked OK for me.
May be you should show the output of this much of the script on your machine?

Resources