Checking if the string is in proper format in Shell Scripting - shell

I am trying to check if the string is in format in shell script.
below is the code i am trying and the output i want to get.
Format: <datatype>(length,length) | <datatype>(length,length)
I have multiple cases with this scenario, if both datatypes have () then it should show pass, else fail.
Eg. decimal(1,0)|number(11,0) this should pass but int|number(11,0) or decimal(1,0)|int should fail.
Code1:
INPUT='decimal(1,0)|number(11,0)'
sub="[A-Z][a-z]['!##$ %^&*()_+'][0-9][|][A-Z][a-z]['!##$ %^&*()_+'][0-9][|]"
if [ "$INPUT" == "$sub" ]; then
echo "Passed"
else
echo "No"
fi
Code 2:
INPUT='decimal(1,0)|number(11,0)'
sub="decimal"
if [ "$INPUT" == *"("*") |"*"("*") " ]; then
echo "Passed"
else
echo "No"
fi
Any help will be fine. Also note, I am very new to shell scripting.

Reading both values into variables, first removing alpha characters and then checking variables are not empty
result='FAIL'
input='int|number(6,10)'
IFS="|" read val1 val2 <<<"$(tr -d '[:alpha:]' <<<"$input")"
if [ -n "$val1" ] && [ -n "$val2" ]; then
result='PASS'
fi
echo "$result: val1='$val1' val2='$val2'"
Result:
FAIL: val1='' val2='(6,10)'
For input='decimal(8,9)|number(6,10)'
PASS: val1='(8,9)' val2='(6,10)'

That looks like just a simple regex to write.
INPUT='decimal(1,0)|number(11,0)'
if printf "%s" "$INPUT" | grep -qEx '[a-z]+\([0-9]+,[0-9]+\)(\|[a-z]+\([0-9]+,[0-9]+\))*'; then
echo "Passed"
else
echo "No"
fi

Related

How can I pipe output, from a command in an if statement, to a function?

I can't tell if something I'm trying here is simply impossible or if I'm really lacking knowledge in bash's syntax. This is the first script I've written.
I've got a Nextcloud instance that I am backing up daily using a script. I want to log the output of the script as it runs to a log file. This is working fine, but I wanted to see if I could also pipe the Nextcloud occ command's output to the log file too.
I've got an if statement here checking if the file scan fails:
if ! sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all; then
Print "Error: Failed to scan files. Are you in maintenance mode?"
fi
This works fine and I am able to handle the error if the system cannot execute the command. The error string above is sent to this function:
Print()
{
if [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "No" ]; then
echo "$1" | tee -a "$log_file"
elif [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "Yes" ]; then
echo "$1" >> "$log_file"
elif [[ "$logging" -eq 0 ]] && [ "$quiet_mode" = "No" ]; then
echo "$1"
fi
}
How can I make it so the output of the occ command is also piped to the Print() function so it can be logged to the console and log file?
I've tried piping the command after ! using | Print without success.
Any help would be appreciated, cheers!
The Print function doesn't read standard input so there's no point piping data to it. One possible way to do what you want with the current implementation of Print is:
if ! occ_output=$(sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all 2>&1); then
Print "Error: Failed to scan files. Are you in maintenance mode?"
fi
Print "'occ' output: $occ_output"
Since there is only one line in the body of the if statement you could use || instead:
occ_output=$(sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all 2>&1) \
|| Print "Error: Failed to scan files. Are you in maintenance mode?"
Print "'occ' output: $occ_output"
The 2>&1 causes both standard output and error output of occ to be captured to occ_output.
Note that the body of the Print function could be simplified to:
[[ $quiet_mode == No ]] && printf '%s\n' "$1"
(( logging )) && printf '%s\n' "$1" >> "$log_file"
See the accepted, and excellent, answer to Why is printf better than echo? for an explanation of why I replaced echo "$1" with printf '%s\n' "$1".
How's this? A bit unorthodox perhaps.
Print()
{
case $# in
0) cat;;
*) echo "$#";;
esac |
if [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "No" ]; then
tee -a "$log_file"
elif [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "Yes" ]; then
cat >> "$log_file"
elif [[ "$logging" -eq 0 ]] && [ "$quiet_mode" = "No" ]; then
cat
fi
}
With this, you can either
echo "hello mom" | Print
or
Print "hello mom"
and so your invocation could be refactored to
if ! sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all; then
echo "Error: Failed to scan files. Are you in maintenance mode?"
fi |
Print
The obvious drawback is that piping into a function loses the exit code of any failure earlier in the pipeline.
For a more traditional approach, keep your original Print definition and refactor the calling code to
if output=$(sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all 2>&1); then
: nothing
else
Print "error $?: $output"
Print "Error: Failed to scan files. Are you in maintenance mode?"
fi
I would imagine that the error message will be printed to standard error, not standard output; hence the addition of 2>&1
I included the error code $? in the error message in case that would be useful.
Sending and receiving end of a pipe must be a process, typically represented by an executable command. An if statement is not a process. You can of course put such a statement into a process. For example,
echo a | (
if true
then
cat
fi )
causes cat to write a to stdout, because the parenthesis put it into a child process.
UPDATE: As was pointed out in a comment, the explicit subprocess is not needed. One can also do a
echo a | if true
then
cat
fi

Save command output only if there's output (Unix CLI)

I have a placeholder file and would like to override it with the output of a command only if the output is not zero length. I guess I could do FOO="$(command)" then [-z $FOO]. Is there a better way?
There are different ways, but I don't know about "better". You could block on a read and only set up the redirection once some data come through:
cmd | { read j && { echo "$j"; cat; } > placeholder; }
(Note, if your command generates output but no newlines, this will ignore the data.)
If you don't need the output of the command, you can run: [ -z "$(command)" ] directly. For example, the following prints "empty":
#!/bin/sh
if [ -z "$(echo -n)" ]; then
echo "empty"
fi
Your example with -z will work, but you can also check if a variable is non-empty just with
[ "$var" ]
So, a simple solution could look like this:
#!/bin/sh
output="$( command )"
[ "$output" ] && echo "$output" > your_file.txt
If you are going to do this type of thing a lot, better make a function:
write_if_non_zero(){
local msg=$1
local file=$2
if [[ ! -z "$msg" ]]; then
echo "$msg" > "$file"
fi
}
Then
write_if_non_zero "$FOO" "$FILE"

While file doesn't contain string BASH

I am making a script for my school, and I was wondering how I could check a file, and if the string isn't in the file, do a code, but if it is, continue, like this:
while [ -z $(cat File.txt | grep "string" ) ] #Checking if file doesn't contain string
do
echo "No matching string!, trying again" #If it doesn't, run this code
done
echo "String matched!" #If it does, run this code
You can do something like:
$ if grep "string" file;then echo "found";else echo "not found"
To do a loop :
$ while ! grep "no" file;do echo "not found";sleep 2;done
$ echo "found"
But be carefull not to enter an infinite loop. string or file must be changed otherwise the loop has no meaning.
Above if/while works based on the return status of the command and not on the result.
if grep finds string in file will return 0 = success = true
if grep does not find string will return 1 = not success = false
By using ! we revert the "false" to "true" to keep the loop running since while loops on something as soon as it is true.
A more conventional while loop would be similar to your code but without the useless use of cat and the extra pipe:
$ while [ -z $(grep "no" a.txt) ];do echo "not found";sleep 2;done
$ echo "found"
A simple if statement to test if a 'string' is not in file.txt:
#!/bin/bash
if ! grep -q string file.txt; then
echo "string not found in file!"
else
echo "string found in file!"
fi
The -q option (--quiet, --silent) ensures output is not written to standard output.
A simple while loop to test is a 'string' is not in file.txt:
#!/bin/bash
while ! grep -q string file.txt; do
echo "string not found in file!"
done
echo "string found in file!"
Note: be aware of the possibility that the while loop may cause a infinite loop!
Another simple way is to just do the following:
[[ -z $(grep string file.file) ]] && echo "not found" || echo "found"
&& means AND - or execute the following command if the previous is true
|| means OR - or execute if the previous is false
[[ -z $(expansion) ]] means return true if the expansion output is null
This line is much like a double negative, basically:
"return true if the string is not found in file.file; then echo not found if true, or found if false"
Example:
bashPrompt:$ [[ -z $(grep stackOverflow scsi_reservations.sh) ]] && echo "not found" || echo "found"
not found
bashPrompt:$ [[ -z $(grep reservations scsi_reservations.sh) ]] && echo "not found" || echo "found"
found

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) ?

Resources