How to put the log of a bash command in a variable? - linux

There is script-one.sh with the following content:
sh script-two.sh
# I need a here a way to store the log generated by the script-two is a variable
script-two.sh logs in terminal the string 'hello'.

Just use $( ) aka command substitution, so :
x="$(sh script-two.sh)"
echo "$x"
As said in the comment by #Walter, double quotes are needed :
"Double quote" every literal that contains spaces/metacharacters and every expansion: "$var", "$(command "$var")", "${array[#]}", "a & b". Use 'single quotes' for code or literal $'s: 'Costs $5 US', ssh host 'echo "$HOSTNAME"'. See
http://mywiki.wooledge.org/Quotes
http://mywiki.wooledge.org/Arguments
http://wiki.bash-hackers.org/syntax/words
Edit :
If you need to catch standard output (stdout), then my command is OK.
If you need instead stderr, then:
x="$(sh script-two.sh 2>&1 >/dev/null)"
Or if you want both, then :
x="$(sh script-two.sh 2>&1)"

Related

Passing string with a space between words through various function layers

I have two functions in Bash. One is a generic run function, that accepts an input and evaluates it, while printing the command, and testing the exit code. This is used in a large script to ensure each command executes successfully before continuing.
The second one is a complex function, that is doing some Git history parsing. The problematic line is the only one shown.
I am calling this function from a for-loop, that iterates over a list of terms to search. The issue is that spaces are not being handled correctly, when between other words. I have tried running my script though shell-checking websites, and all of the suggestions seem to break my code.
function run() {
echo "> ${1}"
eval "${1}"
# Test exit code of the eval, and exit if non-zero
}
function searchCommitContents() {
run 'result=$(git log -S'"${1}"' --format=format:%H)'
# Do something with result, which is a list of matching SHA1 hashes for the commits
echo "${result}"
}
# Main
declare -a searchContents=('foo' 'bar' ' foo ' 'foo bar')
for i in "${searchContents[#]}"
do
searchCommitContents "${i}"
done
Here is the output I get:
> result=$(git log -Sfoo --format=format:%H)
<results>
> result=$(git log -Sbar --format=format:%H)
<results>
> result=$(git log -S foo --format=format:%H)
<results>
> result=$(git log -Sfoo bar --format=format:%H)
fatal: ambiguous argument 'bar': unknown revision of path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
I tried to add additional single and double-quotes to various areas of the code, such that the 'foo bar' string would not resolve to two different words. I also tried adding an escape to the dollar sign, like so: -s'"\${1}"' based on other questions on this site.
Why are you printing result=$(? It's an internal variable, it can be anything, there is no need for it in logs.
Print the command that you are executing, not the variable name.
run() {
echo "+ $*" >&2
"$#"
}
searchCommitContents() {
local result
result=$(run git log -s"${1}" --format=format:%H)
: do stuff to "${result}"
echo "$result"
}
issue with an input that has a space in the middle.
If you want quoted string, use printf "%q" or ${...#Q} for newer Bash, but I don't really enjoy both quoting methods and just use $*. I really like /bin/printf from GNU coreutils, but it's a separate process... while ${..#Q} is the fastest, it's (still) not enough portable for me (I have some old Bash around).
# compare
$ set -- a 'b c' d
$ echo "+ $*" >&2
+ a b c d
$ echo "+$(printf " %q" "$#")" >&2
+ a b\ \ c d
$ echo "+" "${##Q}" >&2
+ 'a' 'b c' 'd'
$ echo "+$(/bin/printf " %q" "$#")" >&2
+ a 'b c' d
See these lines:
> result=$(git log -Sfoo bar --format=format:%H)
fatal: ambiguous argument 'bar': unknown revision of path not in the working tree.
Specifically this: -Sfoo bar. It should be -S"foo bar" or -S "foo bar". Because to pass an argument with spaces, we need to quote the argument. But, each time the argument pass through a command/function layer, one layer of quote ('', "") is extracted. So, we need to nest the quote.
So in this line:
declare -a searchContents=('foo' 'bar' ' foo ' 'foo bar')
change 'foo bar' to '"foo bar"' or "'foo bar'" or "\"foo bar\"".
This is a case of 2 layers nested quotes. The more the layer, the trickier it gets. Here's an example of 4 layers quotes I once did.

Inserting the date into a "MAIL" message body in a BASH script

I have a relatively simple BASH script to send mail from my Raspberry Pi. The first argument is the Subject line and the second is a string of data files to be attached.
It is basically working when I specify the message body as a file (line 6). But if I try to create a text sting containing the date as the message body it fails (line7). Here is my script:
#!/bin/bash
#echo $2
# To
TO="me#hotmail.com"
# Message
MESSAGE="output/MessageBody.txt"
MESSAGEx="Midnight `date '+%Y-%m-%d %H:%M:%S %Z'` Pi report"
echo $MESSAGE
echo $MESSAGEx
temp=$(echo $2 | tr ";" "\n")
declare -a attargs
for att in $temp; do
attargs+=( "-A" "$att" )
done
# Sending email using /bin/mail
/usr/bin/mail -s "$1" "$TO" ${attargs[#]} < $MESSAGEx
Here is the output from this command
/usr/pgms/sendtome.sh "test message" "/mnt/usbdrive/output/JSONstart.txt;/mnt/usbdrive/output/Outback_error.log;/mnt/usbdrive/output/OutbackReaderPrint.txt"
when I specify MESSAGEx as the message body:
/mnt/usbdrive/output/MessageBody.txt
Midnight 2019-08-14 07:40:31 MDT Pi report
/usr/pgms/sendtome.sh: line 22: $MESSAGEx: ambiguous redirect
If I use MESSAGE, ie the text file reference, it works.
How can it create a message body text paragraph which contains the date or some other item? Thanks....RDK
There's a number of issues here.
You should generally quote strings. Without quoting, the string after < is split (hence the error message) and the array you took so much care to collect will lose its purpose.
The thing after < needs to be the name of a file. In Bash you can use a here string <<<"$MESSAGEx" but the common and simple portable solution is to echo (or better printf) its value into a pipe.
You should prefer lower case for your private variable names, but this is mainly a stylistic recommendation. (There are reserved variables like PATH and SHELL which you really don't want to clobber; POSIX reserves upper case variable names for system use.)
Here's a refactoring which attempts to address these concerns.
#!/bin/bash
to="me#hotmail.com"
# Message
#msgfile="output/MessageBody.txt"
msgbody="Midnight `date '+%Y-%m-%d %H:%M:%S %Z'` Pi report"
#echo "$msgfile"
#echo "$msgbody"
declare -a attargs
for att in $(echo "$2" | tr ";" "\n"); do
attargs+=( "-A" "$att" )
done
/usr/bin/mail -s "$1" "${attargs[#]}" "$to"<<< "$msgbody"
Perhaps a better design would be to just shift the first argument and then use "$#" as the list of files to attach.

How to iterate over the arguments of type string in the bash script?

An argument is passed to bash script from outside and is read within bash file. Looks like as follows:
#following is the point from where the argument is passed to config.sh
controller.vm.provision :shell, path: 'shell/config.sh', keep_color: true, privileged: false, :args => ip
inside the config.sh reading the argument. The argument "ip" is somewhat like following:
ip = "10.12.153.26" "10.12.153.25" "10.12.153.24"
Now i want to iterate over the above argument inside the bash. so doing as follows:
array=($1) //please note $1="10.12.153.26" "10.12.153.25" "10.12.153.24"
for i in ${array[#]}
do
echo $i //it is iterated only once and output is "10.12.153.26" "10.12.153.25" "10.12.153.24"
done
So the output i am getting only once and is complete argument as it is but i want it to get displayed one by one so, that i can even use the single value separately for some other purpose. So, please suggest how can i have this?
If you want to split the value on spaces, you can do like this:
set -- $1
for i; do
echo $i
done
If the value of $i is "10.12.153.26" "10.12.153.25" "10.12.153.24",
then this will output:
"10.12.153.26"
"10.12.153.25"
"10.12.153.24"
To get rid of the double-quotes, you could use parameter expansion:
set -- $1
for i; do
echo ${i//\"/}
done

Why does an empty string get enclosed within a single quotes in a shell script?

I've written a simple shell script "sample.sh" as below
#!/bin/bash
PARAM1="Parameter1"
PARAM2=\"\"
echo "param1-->[$PARAM1] - param2-->[$PARAM2]"
# sample is a compiled binary that just prints it's command line arugments.
./sample -param1 $PARAM1 -param2 $PARAM2
The script is run with -x option as
bash -x sample.sh
The output which I got is
[tspot#raspberrypi : ~/src/sample]$ bash -x sample.sh
+ PARAM1=Parameter1
+ PARAM2='""'
+ echo 'param1-->[Parameter1] - param2-->[""]'
param1-->[Parameter1] - param2-->[""]
+ ./sample -param1 Parameter1 -param2 '""'
arg[0] - [./sample]
arg[1] - [Parameter1]
arg[2] - [""]
[tspot#raspberrypi : ~/src/sample]$
My doubt is why do we get a single quote surrounding the empty string in -param2 in the below line
+ ./sample -param1 Parameter1 -param2 '""'
I would need the line to be
+ ./sample -param1 Parameter1 -param2 ""
Thanks in Advance. Someone please help me out.
That's just the way bash -x formats things in it's debug output. It's adding the extra ' ' to indicate that the string is literally "" and not an empty string. If you look a few lines below, you can see that ./sample does have the expected output: arg[2] - [""].
The problem is that your variable $PARAM2 is the string "" (literally two double quotes).
For what you want, I think you just need to initialize it with :
PARAM2=""
# Or
PARAM2=''
Both are equivalent here.
FYI, the difference between single ' and double " quotes in Bash, is that you can put variable $foo in double quotes and they will be evaluated, whereas it will show literal $ in single quotes :
$ foo=bar
$ echo ">$foo<"
>bar<
$ echo '>$foo<'
>$foo<

how to declare variable name with "-" char (dash ) in linux bash script

I wrote simple script as follow
#!/bin/bash
auth_type=""
SM_Read-only="Yes"
SM_write-only="No"
echo -e ${SM_Read-only}
echo -e ${SM_Write-only}
if [ "${SM_Read-only}" == "Yes" ] && [ "${SM_Write-only}" == "Yes" ]
then
auth_type="Read Write"
else
auth_type="Read"
fi
echo -e $auth_type
And when i execute it i got following output with errors.
./script.bash: line 5: SM_Read-only=Yes: command not found
./script.bash: line 6: SM_write-only=No: command not found
only
only
Read
Any one know correct way to declare the variable with "-" (dash)?
EDIT:
have getting response from c code and evaluate the variables for example
RESP=`getValue SM_ Read-only ,Write-only 2>${ERR_DEV}`
RC=$?
eval "$RESP"
from above scripts code my c binary getValue know that script want Read-only and Write-only and return value to script.So during eval $RESP in cause error and in my script i access variable by
echo -e ${SM_Read-only}
echo -e ${SM_Write-only}
which also cause error.
Rename the variable name as follows:
SM_Read_only="Yes"
SM_write_only="No"
Please, don't use - minus sign in variable names in bash, please refer to the answer, on how to set the proper variable name in bash.
However if you generate the code, based on others output, you can simply process their output with sed:
RESP=$(getValue SM_ Read-rule,Write-rule 2>${ERR_DEV}|sed "s/-/_/g")
RC=$?
eval "$RESP"
- is not allowed in shell variable names. Only letters, numbers, and underscore, and the first character must be a letter or underscore.
I think you cant have a dash in your variables names, only letters, digits and "_"
Try:
SM_Read_only
Or
SM_ReadOnly

Resources