Prompt to continue if no argument passed - bash

I want to prompt to do something or not. And if a specific argument such as "-y" or "--yes" is passed I want to make the script non-interactive (force user answer).
if [ $# = 1 ] && [ "$1" = "-y" ]; then
# my code here
else
read -n 1 -p "¿Install this? [y/N] "
if [[ $REPLY =~ ^([Yy])$ ]]; then
# same code here
fi
fi
If I had to use a function I would like it to be something not to do with the code but with the test as I have a lot of this tests in the script.
function(argument)
{
if [ $# = 1 ] && [ "$1" = "-y" ]; then
return true
else
read -n 1 -p "$argument [y/N] "
if [[ $REPLY =~ ^([Yy])$ ]]; then
return true
fi
fi
}
if function("¿Install this?"); then
# my code here
fi
This function is wrong because it overrides the script's argument with the function call's argument.
install_maybe () {
echo $# $1
if [ $# = 1 ] && [ "$1" = "-y" ]; then
return 0
else
read -n 1 -p "$1 [y/N] "
if [[ $REPLY =~ ^[Yy]$ ]]; then
return 0
fi
fi
return 1
}
if install_maybe "Install everything?"; then
source "$DOTFILES/install/esential" "-y"
else source "$DOTFILES/install/esential"
fi

Simply define a flag in a variable that indicates the presence / absence of -y, the auto-confirmation option, among the command-line arguments, and pass that flag to your helper function as a 2nd argument:
#!/bin/bash
# Helper function to ensure that the user has confirmed the intent to proceed.
assertConfirmation() {
local promptMsg=$1 autoConfirm=$2
if (( autoConfirm )); then
return
else
read -n 1 -p "$promptMsg [y/N] "
printf '\n' # Output a newline, because none was appended to the user's keypress.
if [[ $REPLY =~ ^([Yy])$ ]]; then
return
fi
fi
# Getting here means: confirmation was not given - abort the script as a whole.
echo "Aborted." >&2 # Note how the message is sent to *stderr*.
exit 2 # Use a dedicated exit code to signal this condition.
}
# ... Code that performs proper parsing of command-line arguments,
# such as with getopts or GNU getopt, omitted for brevity.
# Here, I assume that the option to signal automatic confirmation
# is in $1, if provided.
[[ $1 == '-y' ]] && autoConfirm=1 || autoConfirm=0
# Call the helper function, which only returns if confirmation is
# either implied by the relevant command-line option or, in its absence,
# by the user confirming the intent to proceed interactively.
assertConfirmation "Install everything?" "$autoConfirm"
# Proceed...
echo "Installing..."

Related

Odd Getopt (1) parsing behavior

Using getopt (1) for parsing some options in bash and it is not evaluating when i pass it options from the cmd line. It is defaulting to the --) "double dash" case statement and passing the other options when i supply them, not sure why it is causing this odd behavior, code below:
parse_args() {
cmd="" # Default none
isparsed=0 # If args parsed = 1
# Check the number of arguments. If none are passed, print help and exit
options=$(getopt -o hs:d:c:p:i: -l help,source:,destination:,connection:,platform:,install: -n "cdr" -- "$#")
# If the number of arguments after processing them using getopt is larger
# than zero, then it means there is an unknown argument.
if [[ $? -ne 0 ]]; then
HELP
exit 1
fi
printdbg "parsing options"
eval set -- "$options"
while true; do
case "$1" in
-h) # Show help option menu
HELP
exit 1
;;
-s|--source) # Parse options to source sub command
cmd=$2 # Remove 'source' from the argument list
shift;
if [[ $cmd == "sqlite" ]]; then
SOURCE="sqlite"
elif [[ $cmd == "mysql" ]]; then
SOURCE="mysql"
else
HELP
exit 1
fi
isparsed=1
;;
-d|--destination) # Parse options to destination sub command
cmd=$2 # Remove 'destination' from the argument list
shift;
if [[ $cmd == "postgre" ]]; then
DESTINATION="postgre"
elif [[ $cmd == "riak" ]]; then
DESTINATION="riak"
elif [[ $cmd == "both" ]]; then
DESTINATION="both"
else
HELP
exit 1
fi
isparsed=1
;;
-c|--connection) # Parse options to connection sub command
cmd=$2 # Remove 'connection' from the argument list
shift; printdbg "$cmd"
if [[ ! -z $cmd ]]; then
SOURCE_CONN=$(echo "$cmd" | awk -F "::" '{print $1}')
DESTINATION_CONN=$(echo "$cmd" | awk -F "::" '{print $2}')
parse_csv "$SOURCE_CONN" #stored in PARSED_ARR
echo ${PARSED_ARR[#]}
# ${DESTINATION_CONN:=${PARSED_ARR}}
else
HELP
exit 1
fi
isparsed=1
;;
-p|--platform) # Parse options to platform sub command
cmd=$2 # Remove 'platform' from the argument list
shift;
if [[ $cmd == "csv" ]]; then
CDR_TYPE=1
elif [[ $cmd == "api" ]]; then
CDR_TYPE=2
elif [[ $cmd == "freeswitch" ]]; then
CDR_TYPE=3
elif [[ $cmd == "asterisk" ]]; then
CDR_TYPE=4
elif [[ $cmd == "yate" ]]; then
CDR_TYPE=5
elif [[ $cmd == "kamailio" ]]; then
CDR_TYPE=6
elif [[ $cmd == "opensips" ]]; then
CDR_TYPE=7
elif [[ $cmd == "sipwise" ]]; then
CDR_TYPE=8
elif [[ $cmd == "veraz" ]]; then
CDR_TYPE=9
else
HELP
exit 1
fi
isparsed=1
;;
-i|--install) # Parse options to install sub command
cmd=$2 # Remove 'install' from the argument list
shift;
if [[ $cmd == "sqlite" ]]; then
install_dependencies
install_sqlite
elif [[ $cmd == "mysql" ]]; then
install_dependencies
install_mysql
elif [[ $cmd == "postgre" ]]; then
install_dependencies
install_postgre
elif [[ $cmd == "riak" ]]; then
printwarn "This feature will be supported in future versions"
exit 1
elif [[ $cmd == "pusher" ]]; then
install_dependencies
install_golang
install_cdrpusher
install_supervisord
elif [[ $cmd == "stats" ]]; then
install_dependencies
install_golang
install_cdrstats
else
HELP
exit 1
fi
isparsed=1
;;
--)
shift
break
;;
esac
done
}
I can tell it is defaulting to that point in the case statement because the isparsed flag is still not set ( == 0 ) and i print the error to the console from my main()
main() {
. cdr_funcs
check_permissions
detect_os
parse_args
if [[ ${isparsed} == 1 ]]; then
INSTALL_COMPLETE_MESG
exit 0
fi
printerr "isparsed flag == 0"
HELP
exit 1
}
Running with an -h flag shows this blatantly:
devbox cdr # ./cdr -h
[DEBUG] [check_permissions]: Root Permissions Validated
[DEBUG] [detect_os]: Starting OS Platform detection
[DEBUG] [detect_os]: Checking OS Compatibility
[DEBUG] [detect_os]: OS detected: [linux] (OS is supported)
[DEBUG] [detect_os]: Finished OS detection
[DEBUG] [parse_args]: parsing options
[ERROR] [main]: isparsed flag == 0
Inside a function (parse_args), "$#" expands to the arguments from the invocation of the function, not the enclosing script. There aren't any, so getopt sees no arguments, and produces no options.
If you want to analyse the script arguments with a function, you need to provide them to the function:
parse_args "$#"

if condition inside function is not working as desired when function called with command line arguments inside find statement

#!/bin/bash
# Code to generate script usage
if [[ "$#" -ne 1 ]] && [[ "$#" -ne 2 ]]; then
flag=1;
elif ! [[ "$1" == "abcd" || "$1" == "dcba" ]]; then
echo "Invalid"
flag=1;
fi
while [ $# -gt 1 ]
do
case $2 in
'streams')
;;
*)
echo "unrecognised optional arg $2"; flag=1;
;;
esac
shift
done
if [ "$flag" == "1" ]; then
echo "Usage:"
exit
fi
function main {
arg1=$1
streams=$2
if [ "${streams}" == "streams" ]; then
echo entering here
else
echo entering there
fi
}
parent_dir=`pwd`
find $parent_dir -name "*" -type d | while read d; do
cd $denter code here
main $1 $2
done
Why the code does not enter "entering here" when script run with arguments "abcd" and "streams" ?
I feel that function having two arguments is causing the problem, code was working fine with one argument
Several things you might want to fix in your code, before attempts are made to find the specific problem. It is possible that it will disappear after modifying your script accordingly. If the problem is still alive, I'll edit my answer with a solution. If you decide to apply the following changes, please update your code in the question.
Consistent usage of either [[ or [. [[ is a Bash keyword similar to (but more powerful than) the [ command.
See
Bash FAQ 31
Tests And Conditionals
Unless you're writing for POSIX sh, I recommend [[.
Use (( for arithmetic expressions. ((...)) is an arithmetic command, which returns an exit status of 0 if the expression is nonzero, or 1 if the expression is zero. Also used as a synonym for let, if assignments are needed. See Arithmetic Expression.
Use the variable PWD instead of pwd. PWD is a builtin variable in all POSIX shells that contains the current working directory. pwd(1) is a POSIX utility that prints the name of the current working directory to stdout. Unless you're writing for some non-POSIX system, there is no reason to waste time executing pwd(1) rather than just using PWD.
The function keyword is not portable. I suggest you to avoid using it and simply write function_name() { your code here; } # Usage
$parent_dir is not double-quoted. "Double quote" every literal that contains spaces/metacharacters and every expansion: "$var", "$(command "$var")", "${array[#]}", "a & b". See
Quotes
Arguments
ShellCheck your code before uploading.
Replace the while condition logic with an if condition, so that shift is no longer required. Shift was the devil I was facing I found.
#!/bin/bash
# Code to generate script usage
if [[ "$#" -ne 1 ]] && [[ "$#" -ne 2 ]]; then
flag=1;
elif ! [[ "$1" == "abcd" || "$1" == "dcba" ]]; then
echo "Invalid"
flag=1;
fi
#while [[ $# -gt 1 ]]
#do
# case $2 in
# 'streams')
# ;;
# *)
# echo "unrecognised optional arg $2"; flag=1;
# ;;
# esac
# shift
#done
if [[ $2 == "streams" ]]; then
:
elif [[ (-z $2) ]]; then
:
else
echo "unrecognised optional arg $2"; flag=1;
fi
if [[ "$flag" == "1" ]]; then
echo "Usage:"
exit
fi
function main {
streams=$2
if [[ "${streams}" == "streams" ]]; then
echo entering here
else
echo entering there
fi
}
parent_dir=`pwd`
find $parent_dir -name "*" -type d | while read d; do
cd $d
main $1 $2
done

press y to continue, any other key to exit (Shell script)

I've written below code, but it won't work for SPACE,special symbols etc.
I want my script to exit if any key pressed on keyboard apart from Y/y.
It should handle SPACEBAR,special symbols
echo "enter y, any other key to exit "
read input
if [ $input != "Y" -o $input = "y" ]; then
echo "Exiting"
fi
if [ $input == "Y" -o $input == "y" ]; then
echo "Working"
fi
See http://wiki.bash-hackers.org/commands/builtin/read?s[]=read
Normally you always want to use the -r flag. -n 1 tells read to only read 1 character.
asksure() {
echo -n "Are you sure (Y/N)? "
while read -r -n 1 -s answer; do
if [[ $answer = [YyNn] ]]; then
[[ $answer = [Yy] ]] && retval=0
[[ $answer = [Nn] ]] && retval=1
break
fi
done
echo # just a final linefeed, optics...
return $retval
}
### using it
if asksure; then
echo "Okay, performing rm -rf / then, master...."
else
echo "Pfff..."
fi
First, you need to quote the parameter expansions to make sure certain inputs are not removed altogether during word splitting and quote removal.
You are using the wrong operator. No matter what key you type, one of not Y and not y will be true. For example, "Y" != "y". You want to use and instead of or.
if [ "$input" != "Y" ] && [ "$input" != "y" ]; then
The boolean operators -o and -a are no longer recommended, due to ambiguities than can arise from their use. Use separate test commands joined by || and && (respectively) instead.
You need to put $input in quotes, so the statement parses properly. If your response is a space, what the script sees is
if [ != "Y" -o != "y" ]; then
Which it obviously can't parse properly.
Use
if [ "$input" != "Y" -o "$input" = "y" ]; then
and you should be OK.
By the way, you don't actually exit if the condition is met. Add a return after the echo "Exiting" line.

How to validate shell arguments?

I am trying to write a simple sh script that must be invoked with 2 arguments:
sh myscript.sh --user "some user" --fizz "buzz"
At the top of myscript.sh I have:
#!/bin/sh
# VALIDATION
# 1. Make sure there are 5 positional arguments (that $4 exists).
die () {
echo >&2 "$#"
exit 1
}
[ "$#" -eq 5 ] || die "5 arguments required, $# provided"
# 2. Make sure $1 is "-u" or "--user".
# 3. Make sure $3 is "-f" or "--fizz".
If validation fails, I'd like to print a simple usage message and then exit the script.
I think I have #1 correct (checking # of positional arguments), but have no clue how to implement #2 and #3. Ideas?
# 2. Make sure $1 is "-u" or "--user".
if ! [ "$1" = -u -o "$1" = --user ]; then
# Test failed. Send a message perhaps.
exit 1
fi
# 3. Make sure $3 is "-f" or "--fizz".
if ! [ "$3" = -f -o "$3" = --fizz ]; then
# Test failed. Send a message perhaps.
exit 1
fi
Other forms for testing a variable for two possible possible values:
[ ! "$var" = value1 -a ! "$var" = value2 ]
[ ! "$var" = value1 ] && [ ! "$var" = value2 ]
! [ "$var" = value1 && ! [ "$var" = value2 ]
For Bash and similarly syntaxed shells:
! [[ $var = value1 || $var = value2 ]]
[[ ! $var = value1 || ! $var = value2 ]]
Besides using negated conditions with if blocks, you can also have positive conditions with ||
true_condition || {
# Failed. Send a message perhaps.
exit 1
}
true_condition || exit 1
Of course && on the other hand would apply with negated conditions.
Using case statements:
case "$var" in
value1|value2)
# Valid.
;;
*)
# Failed.
exit 1
;;
esac
Manually:
if [ -z "$1" ];then
fi
if [ -z "$2" ];then
fi
if [ -z "$3" ];then
fi
...
Or check getopt
while getopts "uf" OPTION
do
case $OPTION in
u)
echo "-u"
;;
f)
echo "-f"
;;
esac
done

bash yes no function

I have many Yes/No answers in my script.
How can I create a function to minimize the size of my script?
I have the following:
function ask {
read -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]
then
return 1;
else
exit
echo "Abort.."
fi
}
ask "Continue? [y/N] "
It works fine. But the Question "Continue? [y/N] is not displayed. How can I "transfer" this text to my function
You can use $1 variable:
function ask {
echo $1 # add this line
read -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]
then
return 1;
else
exit
echo "Abort.."
fi
}
Edit: as noted by #cdarke, 'echo' call can be avoided thanks to '-p' switch in read:
# echo $1
# read -n 1 -r
read -n 1 -r -p "$1"

Resources