read -n 1 -p " , (y/[a]): " AMSURE [ "$AMSURE" = "y" ] || exit echo "" 1>&2
The echo command, by the way, is needed here because after pressing the <y> button you will not have a line feed, therefore, the next output will go to the same line. myAskYN() { local AMSURE if [ -n "$1" ] ; then read -n 1 -p "$1 (y/[a]): " AMSURE else read -n 1 AMSURE fi echo "" 1>&2 if [ "$AMSURE" = "y" ] ; then return 0 else return 1 fi }
The only optional parameter this function takes is the question string. If the string is not set - silent wait for pressing (in cases where the script has already managed to display all that is needed before calling this function). Thus, the application may be such: myAskYN " , ?" || exit
You can write another similar function myAskYNE, with the letter E at the end, in which return is replaced with exit. Then the recording will be even easier: myAskYNE " , ?"
The advantages are obvious: a) write less code, b) code is easier to read, c) do not get distracted by trifles, like the prefix "(y / [a]):" to the test (I note that [a] means any, and taken into square quotes indicates that this is the default). #!/bin/bash a1=myfunc.sh ; source "$a1" ; if [ $? -ne 0 ] ; then echo " — $a1" 1>&2 ; exit 1 ; fi myAskYN " , ?" echo Run!
I deliberately put the whole call and error handling in one line, because this thing is standard and does not relate directly to the logic of the script. Why stretch it half a screen? Note also that the name of the script is assigned to a variable. This allows you to specify the name of the script once, and therefore, you can duplicate the string and replace the library name to connect another library of functions, if necessary. sayWait() { local AMSURE [ -n "$1" ] && echo "$@" 1>&2 read -n 1 -p "( )" AMSURE echo "" 1>&2 } cdAndCheck() { cd "$1" if ! [ "$(pwd)" = "$1" ] ; then echo "!! $1 - . ." 1>&2 exit 1 fi } checkDir() { if ! [ -d "$1" ] ; then if [ -z "$2" ] ; then echo "!! $1 - . ." 1>&2 else echo "$2" 1>&2 fi exit 1 fi } checkFile() { if ! [ -f "$1" ] ; then if [ -z "$2" ] ; then echo "!! $1 - . ." 1>&2 else echo "$2" 1>&2 fi exit 1 fi } checkParm() { if [ -z "$1" ] ; then echo "!!$2. . ." 1>&2 exit 1 fi }
Here I will draw your attention to the constantly occurring combination 1> & 2 after echo. The fact is that your scripts will probably display some valuable information. And this information does not always fit into the screen, and therefore it is not bad to save it to a file or send it to less. The combination 1> & 2 means redirecting output to the standard error device. And when you call the script like this: my-script.sh > out.txt my-script.sh | less
there will be no unnecessary erroneous and service messages, but only what you really want to display. curPath= # , cRes= # pYes= # --yes,
Now we can add another useful function to the collection: input1() { local a1 if [ -n "$1" ] ; then read -p "$1" -sn 1 cRes else read -sn 1 cRes fi # while [ "$2" = "${2#*$cRes}" ] ; do read -sn 1 cRes done echo $cRes 1>&2 }
Here is an example of its use: cat <<'EOF' : ------------------------ a) 1 b) 2 .) EOF input1 " : " "ab." echo " : $cRes"
This function restricts keystrokes to the list of specified ones (for example, a, b, and dot). No other keys will be perceived and nothing will be displayed when they are pressed. The example also shows the use of a return variable ($ cRes). It returns the letter pressed by the user. while [ 1 ] ; do if [ "$1" = "--yes" ] ; then pYes=1 elif [ "${1#--source-file=}" != "$1" ] ; then pSourceFile="${1#--source-file=}" elif [ "$1" = "-sf" ] ; then shift ; pSourceFile="$1" elif [ "${1#--dest-file=}" != "$1" ] ; then pDestFile="${1#--dest-file=}" elif [ "$1" = "-df" ] ; then shift ; pDestFile="$1" elif [ -z "$1" ] ; then break # else echo ": " 1>&2 exit 1 fi shift done checkParm "$pSourceFile" " " checkParm "$pDestFile" " " if [ "$pYes" != "1" ] ; then myAskYNE " , ?" fi echo "source=$pSourceFile, destination=$pDestFile"
This code provides the following features: ./test.sh -sf mysource -df mydest ./test.sh --source-file=mysource --dest-file=mydest ./test.sh --source-file=mysource --dest-file=mydest --yes
procParmS() { [ -z "$2" ] && return 1 if [ "$1" = "$2" ] ; then cRes="$3" return 0 fi return 1 } procParmL() { [ -z "$1" ] && return 1 if [ "${2#$1=}" != "$2" ] ; then cRes="${2#$1=}" return 0 fi return 1 }
At the same time, the parameter processing cycle will look much more digestible: while [ 1 ] ; do if [ "$1" = "--yes" ] ; then pYes=1 elif procParmS "-sf" "$1" "$2" ; then pSourceFile="$cRes" ; shift elif procParmL "--source-file" "$1" ; then pSourceFile="$cRes" elif procParmS "-df" "$1" "$2" ; then pDestFile="$cRes" ; shift elif procParmL "--dest-file" "$1" ; then pDestFile="$cRes" elif [ -z "$1" ] ; then break # else echo ": " 1>&2 exit 1 fi shift done
In fact, this cycle can be copied from the script to the script without thinking about anything other than the names of the keys and the name of the variable for this key. And in this case they are not repeated and the possibility of error is excluded.Source: https://habr.com/ru/post/158971/
All Articles