unable to comare the output of the function in shell script [duplicate] - bash

This question already has answers here:
Bash script store command output into variable
(2 answers)
Closed 2 years ago.
I am trying to remove docker container and and store or compare the result but i am unable to do that kindly let me know how to get the return value for further process
delete_the_container() {
res= docker container rm -f $1 && echo "success" || echo "fail"
echo "$?"
}
fielpos="./filename"
input="./filename"
while IFS= read -r line
do
#echo "$line"
res= $(delete_the_container $line)
echo "$res" #not get the res value
done < "$input"

$? returns code value of last command, the code below must help you
docker container rm -f $1
if [[ $? == 0 ]]; then
... code if rm command success ...
else
... code if rm command failed...
fi
Your line (I added $(...) to execute command) :
res= $(docker container rm -f $1 && echo "success" || echo "fail")
res value can be "success" or "fail" but $? are all time 1 because last command are echo.

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

How to check if pipe content (stdout) is empty in bash [duplicate]

This question already has answers here:
Test if a command outputs an empty string
(13 answers)
Closed 4 years ago.
Some Example:
I've a shellscript where I want to check if a the stdout of a command is empty.
So I can do
if [[ $( whateverbin | wc -c) == 0 ]] ; then
echo no content
fi
But is there no direct command, to check this? Something like :
if whateverbin | checkifstdinisempty ; then
echo no content
fi
You could use the -z conditional expression to test if a string is empty:
if [[ -z $(ls) ]]; then echo "ls returned nothing"; fi
When you run it on an empty result, the branch gets executed:
if [[ -z $(cat non-existing-file) ]]; then echo "there was no result"; fi
Just try to read exactly one character; on no input, read will fail.
if ! whateverbin | IFS= read -n 1; then
echo "No output"
fi
If read fails, the entire pipeline fails, and the ! negates the non-zero exit status so that the entire condition succeeds.
[[ `echo` ]] && echo output found || echo no output
--> no output
[[ `echo something` ]] && echo output found || echo no output
--> output found
With if :
if [ `echo` ] ; then echo ouput found; else echo no output; fi

Bash : create file with name from function parameter [duplicate]

This question already has answers here:
"~/Desktop/test.txt: No such file or directory"
(2 answers)
Closed 6 years ago.
I want to append things to a file in a way that uses sudo if needed, here's what I have so far :
getAndAppend(){
# create file if doesn't exist, with right permission
[[ ! -s $2 ]] && touch "$2" || [[ ! -s $2 ]] && sudo touch "$2" # line 1
# append stuff to it
[[ -w $2 ]] && curl -sSL $1 >> $2 || sudo bash -c "curl -sSL $1 >> $2"
[[ -w $2 ]] && echo -e "\n" >> $2 || sudo bash -c "echo -e \"\n\""
}
file="~/.wot"
url="https://raw.github.com/n-marshall/system-setup/master/common/configs/.gitignore_global"
getAndAppend $url $file
However line 1 doesn't work in that the output will be something like ~/.wot: No such file or directory
Then of course the file won't exist and the following lines cannot work properly.
How could I fix that ? Thank you ! Any other comment or approach is welcome of course !
file=~/.wot
or
file="$HOME/.wot"
Quotes prevent tilde expansion.
By the way -- you can do better than plastering sudo in front of every command that needs to emit output to your destination file. Consider:
# Note that this requires bash 4.1 for automatic FD allocation
# otherwise, modify it to hardcode a FD number for our backup.
withOutputToFile() {
local orig_stdout retval
exec {orig_stdout}>&1 || return
if ! exec >"$1"; then
if ! exec > >(sudo tee -- "$1"); then
exec >&$orig_stdout
return 1
fi
fi
shift
"$#"; retval=$?
exec >&$orig_stdout {orig_stdout}>&-
return "$retval"
}
That's a mouthful, sure, but once you have it you can use it to wrap any function:
getAndAppend() {
curl "$1" && printf '\n'
}
withOutputToFile /path/to/your/file getAndAppend http://example.com/

[-f: Command not found, Bash script does file exist [duplicate]

This question already has answers here:
Why should there be spaces around '[' and ']' in Bash?
(5 answers)
Closed 6 years ago.
I'm having an issue with a script that I am trying to program. Narrowed down and simplified code and it gives an error that command is not found. If i do "test -f file" in command line it returns nothing, not command not found
PATH=$1
#!/bin/bash
DIR=$1
if [[-f $PATH]]; then
echo expression evaluated as true
else
echo expression evaluated as false
fi
exit
Here is the actual more complicated script I'm trying to run
verify()
{
if [[-f $1]]; then
VFY[$2]="f"
echo "$1 is a file"
elif [[-d $1]]
then
VFY[$2]="d"
echo "$1 is a directory"
else
VFY[$2]=0
echo -e "\r"
echo "$1 is neither a file or a directory"
echo -e "\r"
fi
}
Its part of a larger script that can move things around depending on inputs. I've run this in CentOS 6, and FreeBSD, both give the same error "[[-f: Command not found"
Simply add an extra space between [[ and -f, and also before ]].
You will get:
#! /bin/bash
DIR=${1-} # unused in your example
if [[ -f test.sh ]]; then
echo "expression evaluated as true"
else
echo "expression evaluated as false"
fi
exit
and for your function
verify() # file ind
{
local file=$1 ind=$2
if [[ -f "$file" ]]; then
VFY[ind]="f" # no need of $ for ind
echo "$file is a file"
elif [[ -d "$file" ]]; then
VFY[ind]="d"
echo "$file is a directory"
else
VFY[ind]=0
echo -e "\n$file is neither a file or a directory\n"
fi
}

Error in this bash script

I want to make a build chain script, and I don't want it to perform until the end if there are error during compilation.
It's the first time I write a more "elaborated" script in bash, and it just doesn't work:
it doesn't echo ERROR although I have lines with the word error in it
whatever the value of testError, the script just hangs in the line
this is the code:
testError=false
output=$(scons)
while read -r line; do
if [[ $line == .*[eE]rror.* ]] ; then echo 'ERROR' ; $testError = true ; fi #$testError = true fi
done
echo $testError
if $testError ; then exit ; fi;
... other commands
EDIT: Following all posters answers and Bash setting a global variable inside a loop and retaining its value -- Or process substituion for dummies and How do I use regular expressions in bash scripts?,
this is the final version of the code.
It works:
testError=false
shopt -s lastpipe
scons | while read -r line; do
if [[ $line =~ .*[eE]rror.* ]] ; then
echo -e 'ERROR'
testError=true
fi
echo -e '.'
done
if $testError ; then
set -e
fi
You set the value of testError in a subshell induced by your pipeline. When that subshell exits (at the end of the pipeline), any changes you made disappear. Try this:
while read -r line; do
if [[ $line == .*[eE]rror.* ]] ; then
echo -e 'ERROR'
testError=true
fi #$testError = true fi
done < <( scons )
or, if you don't want or can't use process substitution, use a temporary file
scons > tmp
while read -r line; do
if [[ $line == .*[eE]rror.* ]] ; then
echo -e 'ERROR'
testError=true
fi #$testError = true fi
done < tmp
This eliminates the pipeline, so the changes to testError persist after the while loop.
And, if your version of bash is new enough (4.2 or later), there is an option that allows the while loop at the end of a pipeline to execute in the current shell, not a subshell.
shopt -s lastpipe
scons | while read -r line; do
if [[ $line == .*[eE]rror.* ]] ; then
echo -e 'ERROR'
testError=true
fi #$testError = true fi
done
You should try
set -e
this stops the script to continue if a command exit with a non zero status
or better
error_case() { # do something special; }
trap 'echo >&2 "an error occurs"; error_case' ERR
this run error_case function each time a command exit with a non zero status
See http://mywiki.wooledge.org/BashFAQ/105
Another bug is that you have spaces in the assignment. And skip the $
$testError = true
should be
testError=true
EDIT
testerror is changed in the subshell. Try
testerror=$(
scons | while read -r line; do
if [[ $line == .*[eE]rror.* ]] ; then
echo true
fi #$testError = true fi
done
)
Are you trying to parse the output of scons?
This:
output=$(scons)
while read -r line; do
if [[ $line == .*[eE]rror.* ]] ; then
echo 'ERROR'
testError=true
fi
done
does not do that. Perhaps you want:
scons | while read -r line; do ... ; done
I answer also because other answers didn't notice: the use of regular expression should be done this way, using =~and not ==:
if [[ $line =~ .*[eE]rror.* ]] ; then
...
cf How do I use regular expressions in bash scripts?

Resources