bash scripting one line shortcut for if test - bash

Is there a shortcut (ie all on one line) for doing the following?
if [ -z "$PASSWORD" ] ; then
echo "PASSWORD envvar required. Exiting..."
exit 1
fi
I'm thinking along the lines of the following. Or maybe there's an even shorter way.
[ -z "$PASSWORD" ] && ...
That is, how do put the echo then exit after the &&

You can do it like this:
[ -z "$PASSWORD" ] && { echo "PASSWORD envvar required. Exiting..."; exit 1; }
Note the extra ; at the end of the line before the }, this is necessary and you cannot leave it out.
It's called a command group.

[[ -z "$PASSWORD" ]] && echo "PASSWORD envvar required. Exiting." && exit 1
Your single brackets will work fine. Consider learning to use the doubles, though.

Related

My bash script verification code does not work

I am trying to write a program where the user can enter a username and password, then the code should check if the username and password are correct unfortunately whenever it checks no matter if the username/password is correct of not it will echo "Username Verified".
#!/bin/bash
echo "Username"
read username
echo "Password"
read password
sleep 1
correct_u=user
correct_p=pass
if [[ $username -eq $correct_u ]]
then
echo "Username Verified..."
else
echo "Username Incorect..."
fi
sleep 1
if [[ $correct_p -eq $password ]]
then
sleep 1
echo "Password Verified..."
else
echo "Password Incorect..."
fi
I have tired checking that all the variables work
Unless username and correct_u consist solely of digits, [[ $username -eq $correct_u ]] will always evaluate to true, since -eq forces the arguments to be numbers, and if there are no number, the arguments are treated as zero.
To do a string comparision, do
[[ $username == "$correct_u" ]]
Quoting the right-hand side is important here, to avoid that it is interpreted as glob-pattern, since == in general does a wildcard match.
You should use = instead of -eq when comparing strings in bash. = is used for string comparison while -eq is used for integer comparison:
#!/bin/bash
echo "Username"
read username
echo "Password"
read password
sleep 1
correct_u=user
correct_p=pass
if [[ "$username" = "$correct_u" ]]
then
echo "Username Verified..."
else
echo "Username Incorrect..."
fi
sleep 1
if [[ "$correct_p" = "$password" ]]
then
sleep 1
echo "Password Verified..."
else
echo "Password Incorrect..."
fi
Embedding your name & pw in cleartext in the file isn't ideal. The user ID must be able to read it to execute the commands in it; executable permissions won't help if you take away read.
Use that to your advantage. Set the user/group/world permissions appropriately. Then the user and password are entered at login...
You might want to combine methods from here and here, reading the right password securely from a vault file and comparing that to the one you read silently from the user.
But first, as already mentioned - fix your test.
$: [[ "one" -eq "two" ]] && echo same || echo no
same
$: [[ "one" == "two" ]] && echo same || echo no
no
$: [[ "one" == "one" ]] && echo same || echo no
same

Functional Programming with Bash

How would I implement something similar to functional programming within bash?
I have a function:
requestPassword(){
passwordValidated=false
while [ "$passwordValidated" = "false" ];do
tput clear
[ -n "$infomsg" ] && echo -e "$infomsg" >&2
[ -n "$errmsg" ] && echo -e "$errmsg" >&2
read -s -p "some prompt:" pass >&2
errmsg=$()#do some validation
[ -n "$errmsg" ] && continue
passwordValidated=true
done
echo "$pass"
}
But I want to make it as generic as possible to work with different systems
Maybe I want to ask the password for an encrypted file
Maybe I want to ask the password for a system user
Maybe I want to use this function to request a new password from a user
The idea would be that I could pass this function a function that takes in a pass parameter and only outputs something if validation failed
You can pass a function name as an argument
requestPassword(){
local infomsg="$1"
local validator=${2:-the_default_validator_function}
# ...
local errmsg
errmsg=$( "$validator" "$pass" )
# ...
}
then
# a valid "foobar" password contains the substring "foo"
foobarPasswordValidator() { [[ $1 == *foo* ]]; }
pass=$(requestPassword "Enter your FooBar password" foobarPasswordValidator)
If you don't have a default validator, use : or true -- they accept arguments, ignore them(1) and return a "success" exit status.
(1) ignored after any parameter expansions have occurred
This is my solution. Please feel free to add any more insight you may have on this topic
requestPassword(){
infomsg="$1"
passwordValidated=false
while [ "$passwordValidated" = "false" ];do
tput clear
[ -n "$infomsg" ] && echo -e "$infomsg" >&2
[ -n "$errmsg" ] && echo -e "$errmsg" >&2
read -s -p "some prompt:" pass >&2
errmsg=$(dynPasswordValidation "$pass")
[ -n "$errmsg" ] && continue
passwordValidated=true
done
echo "$pass"
}
You would then have other functions that would call this function for specific password requests but would define dynPasswordValidation function prior to the call like so:
requestCurrentEncryptedFilePassword(){
infomsg="Requesting password for encrypted file"
dynPasswordValidation(){
[ -n "$(openssl $encalg -d -in $encfile -pass "pass:$1" 2>&1>/dev/null)" ] && echo "Looks like that password didn't work" && return
}
pass="$(requestPassword "$infomsg")"
}
requestNewEncryptedFilePassword(){
infomsg="Requesting new password for encrypted file"
dynPasswordValidation(){
pass="$1"
echo >&2
read -p -s "veri prompt" pass_veri >&2
echo >&2
[ "$pass" != "$pass_veri" ] && echo "Looks like the passwords didn't match" && return
[ -n "$(#run the password through a set of password rules)" ] && echo "Looks like the password is not up to snuff..." && return
}
pass="$(requestPassword "$infomsg")"
}

How to check if multiple variables are defined or not in bash

I want to check, if multiple variable are set or not, if set then only execute the script code, otherwise exit.
something like:
if [ ! $DB=="" && $HOST=="" && $DATE=="" ]; then
echo "you did not set any variable"
exit 1;
else
echo "You are good to go"
fi
You can use -z to test whether a variable is unset or empty:
if [[ -z $DB || -z $HOST || -z $DATE ]]; then
echo 'one or more variables are undefined'
exit 1
fi
echo "You are good to go"
As you have used the bash tag, I've used an extended test [[, which means that I don't need to use quotes around my variables. I'm assuming that you need all three variables to be defined in order to continue. The exit in the if branch means that the else is superfluous.
The standard way to do it in any POSIX-compliant shell would be like this:
if [ -z "$DB" ] || [ -z "$HOST" ] || [ -z "$DATE" ]; then
echo 'one or more variables are undefined'
exit 1
fi
The important differences here are that each variable check goes inside a separate test and that double quotes are used around each parameter expansion.
If you are ok with writing a function for this purpose, it can be pretty convenient.
This solution uses the ${!VAR_NAME} syntax to check whether the variable is empty and has the added benefit of telling you which variable names are empty.
check_vars()
{
var_names=("$#")
for var_name in "${var_names[#]}"; do
[ -z "${!var_name}" ] && echo "$var_name is unset." && var_unset=true
done
[ -n "$var_unset" ] && exit 1
return 0
}
# Usage for this case
check_vars DB HOST DATE
echo "You are good to go"
I wound up using variable-variables to loop through an easily managed HEREDOC list of variable names:
# Ensure non-empty values.
# Loop through HEREDOC, test variable-variable isn't blank.
while read var; do
[ -z "${!var}" ] && { echo "$var is empty or not set. Exiting.."; exit 1; }
done << EOF
KUBE_NAMESPACE
DOCKER_REGISTRY
DOCKER_DEPLOY_USER
DOCKER_DEPLOY_PASSWORD
DOCKER_DEPLOY_EMAIL
EOF
You can check it also by put the variables name in a file
DB=myDB
HOST=myDB
DATE=myDATE
then test them if currently empty or unset
#!/bin/bash
while read -r line; do
var=`echo $line | cut -d '=' -f1`
test=$(echo $var)
if [ -z "$(test)" ]; then
echo 'one or more variables are undefined'
exit 1
fi
done <var.txt
echo "You are good to go"
Nice solution from #joe.still !
improvement is to exit after checking all variables
i=0
while read var; do
[ -z "${!var}" ] && { echo "$var is empty or not set. Exiting.."; let i=i+1; }
done << EOF
KUBE_NAMESPACE
DOCKER_REGISTRY
DOCKER_DEPLOY_USER
DOCKER_DEPLOY_PASSWORD
DOCKER_DEPLOY_EMAIL
EOF
if [ $i -gt 0 ]; then
echo $i
echo "exiting"
exit 1
fi
Good Day Everyone.
I've personally used this method in my bash scripts. Verified works on bash 4.4 and later in Ubuntu, openSUSE, and ClearLinux.
Can RHEL|CentOS|Alma and Arch Based users let me know it it works fine for you?
( [ "$VAR1""$VAR2""$VAR3""$VAR4""$VAR5" ] && echo -e " Warning: StackIsNotClear" ) || { echo -e " GoodNews: StackIsClear"; }

bash determine if variable is empty and if so exit.

I am trying to perform this:
i have a test file which md5sum of files located on sftp.
variables should contain an md5sum (string), if the variable is empty it means there is no file on the sftp server.
i am trying this code but it does not work..
if [ -z $I_IDOCMD5 ] || [ -z $I_LEGALMD5 ] || [ -z $I_ZIPMD5 ]
then
echo "ERROR: At least one file not present of checksum missing no files will be deleted" >>$IN_LOG
ERRORS=$ERRORS+2
else
if [[ $I_IDOCMD5 == $($DIGEST -a md5 $SAPFOLDER/inward/idoc/$I_IDOC) ]]
then
echo "rm IDOC/$I_IDOC" >/SAP/commands_sftp.in
else
echo "problem with checksum"
ERRORS=$ERRORS+2
fi
if [[ $I_LEGALMD5 == $($DIGEST -a md5 $SAPFOLDER/inward/legal/$I_LEGAL) ]]
then
echo "rm LEGAL/$I_LEGAL" >>/SAP/commands_sftp.in
else
echo "problem with checksum"
ERRORS=$ERRORS+2
fi
if [[ $I_ZIPMD5 == $($DIGEST -a md5 $SAPFOLDER/inward/zip/$I_ZIP) ]]
then
echo "rm ZIP/$I_ZIP" >>/SAP/commands_sftp.in
else
echo "problem with checksum"
ERRORS=$ERRORS+2
fi
The answer I prefer is following
[[ -z "$1" ]] && { echo "Parameter 1 is empty" ; exit 1; }
Note, don't forget the ; into the {} after each instruction
One way to check if a variable is empty is:
if [ "$var" = "" ]; then
# $var is empty
fi
Another, shorter alternative is this:
[ "$var" ] || # var is empty
In bash you can use set -u which causes bash to exit on failed parameter expansion.
From bash man (section about set builtin):
-u
Treat unset variables and parameters other than the special parameters "#" and "*" as an error when performing parameter
expansion. If expansion is attempted on an unset variable or
parameter, the shell prints an error message, and, if not interactive,
exits with a non-zero status.
For more information I recommend this article:
http://redsymbol.net/articles/unofficial-bash-strict-mode/
You can use a short form:
FNAME="$I_IDOCMD5"
: ${FNAME:="$I_LEGALMD5"}
: ${FNAME:="$I_ZIPMD5"}
: ${FNAME:?"Usage: $0 filename"}
In this case the script will exit if neither of the I_... variables is declared, printing an error message prepended with the shell script line that triggered the message.
See more on this in abs-guide (search for «Example 10-7»).
First test only this (just to narrow it down):
if [ -z "$I_IDOCMD5" ] || [ -z "$I_LEGALMD5" ] || [ -z "$I_ZIPMD5" ]
then
echo "one is missing"
else
echo "everything OK"
fi
echo "\"$I_IDOCMD5\""
echo "\"$I_LEGALMD5\""
echo "\"$I_ZIPMD5\""
"if the variable is empty it means there is no file on the sftp server"
If there is no file on the sftp server, is the variable then really empty ?
No hidden spaces or anything like that ? or the number zero (which counts as non-empty) ?

Bash, always echo in conditional statement

This may turn out to be more of a thought exercise, but I am trying to echo a newline after some command I'm executing within a conditional. For example, I have:
if ssh me#host [ -e $filename ] ; then
echo "File exists remotely"
else
echo "Does not exist remotely"
fi
And want to throw in an echo after the ssh command regardless of the outcome. The reason is formatting; that way a newline will exist after the prompt for password for ssh.
First Try
if ssh me#host [ -e $filename ] && echo ; then
Because && echo would not change the conditional outcome, but bash would not execute echo if ssh returned false. Similarly,
if ssh me#host [ -e $filename ] || (echo && false) ; then
Does not work because it will short-circuit if ssh returns true.
An answer to the problem would be
ssh me#host [ -e $filename ]
result=$?
echo
if [ $result == 0 ] ; then
but was wondering if there was some similar conditional expression to do this.
Thanks.
While this would work
if foo && echo || ! echo; then
I'd prefer putting the whole thing into a function
function addecho() {
"$#" # execute command passed as arguments (including parameters)
result= $? # store return value
echo
return $result # return stored result
}
if addecho foo; then
What about this?
if ssh me#host [ -e $filename ] && echo || echo; then
I have not thought about precedence order of && and || and surely putting some parenthesis would help, but like that it works already... you get the echo both when ssh fails and when it succeeds...
Add the "echo" before the filename test
if ssh me#host "echo; [ -e $filename ]"; then
echo "File exists remotely"
else
echo "Does not exist remotely"
fi

Resources