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?