Bash output limited to echo only - linux

I am writing a bash script to handle by backups. I have created a message function controller that uses functions to handle email, log and output.
So the structure is as:
message_call(i, "This is the output")
Message Function
-> Pass to email function
--> Build email file
-> Pass to log function
--> Build log file
-> Pass to echo function (custom)
--> Format and echo input dependent on $1 as a switch and $2 as the output message
When I echo I want nice clean output that only consists of messages passed to the echo function, I can point all output /dev/null but I am struggling to limit all output except for the echo command.
Current output sample:
craig#ubuntu:~/backup/functions$ sudo ./echo_function.sh i test
+ SWITCH=i
+ INPUT=test
+ echo_function
+ echo_main
+ echo_controller i test
+ '[' i == i ']'
+ echo_info test
+ echo -e '\e[32m\e[1m[INFO]\e[0m test'
[INFO] test
+ echo test
test
+ '[' i == w ']'
+ '[' i == e ']'
Above I ran the echo function alone and the output I want is on line 10, all other output in the sample I don't want.

If you have the line set -x in your script, comment it out. If not, try adding set +x at the top of your script.

If you want to hide all the output from everything except what you're explicitly doing in your echo function you could do something like this:
exec 7>&1 # save a copy of current stdout
exec >/dev/null # redirect everyone else's stdout to /dev/null
ls # output goes to /dev/null
echo My Message >&7 # output goes to "old" stdout

Related

Simplify debugging output. Specific request for in-place command text replacement in BASH

OK, so, I have a debugging setup a little like this:
DVAR=();
function DBG() {
if [[ $1 == -s ]]; then shift; stackTrace; fi;
if [[ ! -z ${DVAR[#]} ]]; then
for _v in ${!DVAR[#]}; do
echo "${DVAR[$_v]}" >> $LOG;
unset DVAR[$_v];
done;
fi
local tmp=("$*")
[[ ! -z $tmp ]]&&echo "$tmp" >> $LOG||continue;
}
every once in a while I call it either directly or, and I'd like to take this approach more, by repeatedly adding things to the array and calling it later. SPECIFICALLY, I'd like to be using this:
DVAR+="${0##*/}${FUNCNAME[0]}:$LINENO === assorted local variables and stuff here ====="
That first part is quite a mouthful and really clutters up my code. I'd REALLY rather be able to say something like:
DBG === assorted local variables and stuff here=====
I've tried messing around with alias and even eval, all to no. . evail. ahem
Thoughts anyone?
Like #ufopilot said, you should add your line as a new entry in the array with DVAR+=("...."), not overwriting it as a long string by concatenating it to the first element. Here's an explaining it:
Concatenating:
$ DVAR=()
$ DVAR+="foo"
$ DVAR+="bar"
$ declare -p DVAR
declare -a DVAR=([0]="foobar")
$ echo ${DVAR[#]}
foobar
Appending new entry:
$ DVAR=()
$ DVAR+=(foo)
$ DVAR+=(bar)
$ declare -p DVAR
declare -a DVAR=([0]="foo" [1]="bar")
$ echo "${DVAR[#]}"
foo bar
Here's an example of a function I put together for debugging purposes some time. The function is get_stack which will get the function name from whoever called it and the file name of where that calling function exists in, along with the trace so you can see the call history.
File test.sh:
#!/usr/bin/env bash
foo() {
get_stack
echo -e "$stack_trace" # Note the quotation to get indentation
}
get_stack () {
stack_trace=""
local i stack_size=${#FUNCNAME[#]}
local indent=" "
local newline="" # newline only after first line
# Offset to skip get_stack function
for (( i=0; i<$stack_size; i++ )); do
local func="${FUNCNAME[$i]}"
[ x$func = x ] && func=MAIN
local linen="${BASH_LINENO[$(( i - 1 ))]}"
local src="${BASH_SOURCE[$i]}"
[ x"$src" = x ] && src=non_file_source
stack_trace+="${newline}${indent}-> $src [$func]: $linen"
newline="\n"
indent="$indent "
done
}
echo "stack from test.sh"
foo
File test2.sh:
#!/usr/bin/env bash
source test.sh
echo "stack from test2.sh"
foo
Output:
stack from test.sh
-> test.sh [foo]: 4
-> test.sh [source]: 28
-> ./test2.sh [main]: 3
stack from test2.sh
-> test.sh [foo]: 4
-> ./test2.sh [main]: 6
In my script I have a down-right arrow ascii character that looks better than "->" but can't figure out how to get stackoverflow to display it proberly. The ascii is \u21b3.
As you can see in the stack trace, "foo" ran upon sourcing the function, just as it's supposed to do. But now it is clear why it ran and outputed text!
I think this demonstrates well how you can the array FUNCNAME to walk backwards in the call stack. Also BASH_SORUCE is an array itself, with matching indices, however it will display what file the function call came from. Modifying the "get_stack" function to inspect these arrays:
foo() {
get_stack
echo -e "$stack_trace"
echo "${BASH_SOURCE[#]}"
echo "${FUNCNAME[#]}"
}
yields:
stack from test.sh
-> test.sh [foo]: 4
-> test.sh [source]: 30
-> ./test2.sh [main]: 3
test.sh test.sh ./test2.sh
foo source main
stack from test2.sh
-> test.sh [foo]: 4
-> ./test2.sh [main]: 6
test.sh ./test2.sh
foo main
As you can see, the first set of outputs belong to test.sh, which came from the sourcing. This you can see in BASH_SOURCE: "foo source main". The "main" is the main scope, that is the stuff that runs but is not in a function but the main body of the file. In this case, test.sh had the "foo" call which upon sourcing this file will run.
I hope you see how the indices belong to the same call, but yield different info.
Now for your part, you wanted to add only the string and not the whole same-y info again and again. Since I'm not sure you want the stack trace or not I just added the message string to the first calling function instance. I also fixed up some old code here to make it a little better.
New improved function with message:
get_stack () {
local msg="$#"
stack_trace=""
local i stack_size=${#FUNCNAME[#]}
local indent=" "
# Offset to skip get_stack function
for (( i=1; i<$stack_size; i++ )); do
local func="${FUNCNAME[$i]}"
[ x$func = x ] && func=MAIN
local linen="${BASH_LINENO[$(( i - 1 ))]}"
local src="${BASH_SOURCE[$i]}"
[ x"$src" = x ] && src=non_file_source
stack_trace+="${newline:=\n}${indent}-> $src [$func:$linen]${msg:+": $msg"}"
msg=""
indent="$indent "
done
}
Output:
$ ./test2.sh
stack from test.sh
-> test.sh [foo:4]: my message
-> test.sh [source:28]
-> ./test2.sh [main:3]
stack from test2.sh
-> test.sh [foo:4]: my message
-> ./test2.sh [main:6]
Note that ${var:=X} will initialize "var" with "X" if var was uninitialized or set to empty string, ${var:+X} will replace "var" with "X" if var is initialized/set to something that is not the empty string, and finally as bonus ${var:-X} will replace "var" with "X" if var is uninitialized/set empty string. This is variable substitution in bash which is quite handy!
There are some pieces you can cut and past into your function, or if you need a stack based log you can use my function as a base.
Hope this helps you in your endeavors!

A strange error in shell script while combining two string variables

I will post my script here
#!/bin/tcsh
echo 'Running'
set fileN = '2021-02-07-0448-04S.JKH_RR.SAC'
set fileE = '2021-02-07-0448-04S.JKH_RR_BHE.SAC'
set compR=BHR
set compT=BHT
set compR_name=BHR.SAC
set compT_name=BHT.SAC
set fileN_rot = `echo $fileN | awk '{split($0,a,".SAC"); print a[1]}'`
set fileE_rot = `echo $fileE | awk '{split($0,a,".SAC"); print a[1]}'`
echo 'output1'
echo $fileN
echo $fileE
echo 'output2'
echo $fileN_rot
echo $fileE_rot
echo 'output3'
echo $fileE_rot-$compR_name
echo $fileN_rot-$compT_name
The output is:
Running
output1
2021-02-07-0448-04S.JKH_RR_BHN.SAC 2021-02-07-0448-04S.JKH_RR_BHE.SAC
output2
2021-02-07-0448-04S.JKH_RR_BHN
2021-02-07-0448-04S.JKH_RR_BHE
output3
2021-02-07-0448-04S.JKH_RR_BHN
-BHR.SAC
2021-02-07-0448-04S.JKH_RR_BHE-BHT.SAC
echo $fileE_rot-$compR_name giving wrong output.
Here the out is copy-pasted from the output file,so -BHR.SAC showing in new line.
But in shell terminal it is showing -BHR.SAC07-0448-04S.JKH_RR_BHN.
I find it strange.
Looks like you have some control chars in your strings. Run cat -Ev script to see them and if you see ^Ms in the output then read Why does my tool output overwrite itself and how do I fix it? for how to deal with them.
Don't write scripts in [t]csh, though, as it wasn't designed for that. Writing a script in csh is like digging a hole with a toothbrush - sure you CAN kinda get there in the end but there are better alternatives. See https://www.google.com/search?q=google+csh+why+not.
Having said that, it's not obvious why you're trying to manipulate text in any shell. Shells exist to manipulate (create/destroy) files and processes and sequence calls to tools. The people who invented shell also invented tools such as awk for shell to call when appropriate to manipulate text. So, here is how to really write a shell script to do what you want (the shell part is to call awk to manipulate the text):
$ cat tst.sh
#!/usr/bin/env bash
awk '
BEGIN {
print "Running"
fileN = "2021-02-07-0448-04S.JKH_RR.SAC"
fileE = "2021-02-07-0448-04S.JKH_RR_BHE.SAC"
compR = "BHR"
compT = "BHT"
compR_name = "BHR.SAC"
compT_name = "BHT.SAC"
fileN_rot = fileN
sub(/\.SAC$/,"",fileN_rot)
fileE_rot = fileE
sub(/\.SAC$/,"",fileE_rot)
print "output1"
print fileN
print fileE
print "output2"
print fileN_rot
print fileE_rot
print "output3"
print fileE_rot "-" compR_name
print fileN_rot "-" compT_name
}
'
$ ./tst.sh
Running
output1
2021-02-07-0448-04S.JKH_RR.SAC
2021-02-07-0448-04S.JKH_RR_BHE.SAC
output2
2021-02-07-0448-04S.JKH_RR
2021-02-07-0448-04S.JKH_RR_BHE
output3
2021-02-07-0448-04S.JKH_RR_BHE-BHR.SAC
2021-02-07-0448-04S.JKH_RR-BHT.SAC
or if there really was some reason to want to do it directly in a shell (e.g. this code is in some loop manipulating files named based on these variables) then:
$ cat tst.sh
#!/usr/bin/env bash
fileN='2021-02-07-0448-04S.JKH_RR.SAC'
fileE='2021-02-07-0448-04S.JKH_RR_BHE.SAC'
compR='BHR'
compT='BHT'
compR_name='BHR.SAC'
compT_name='BHT.SAC'
fileN_rot="${fileN%*.SAC}"
fileE_rot="${fileE%*.SAC}"
echo 'output1'
echo "$fileN"
echo "$fileE"
echo 'output2'
echo "$fileN_rot"
echo "$fileE_rot"
echo 'output3'
echo "${fileE_rot}-${compR_name}"
echo "${fileN_rot}-${compT_name}"
$ ./tst.sh
output1
2021-02-07-0448-04S.JKH_RR.SAC
2021-02-07-0448-04S.JKH_RR_BHE.SAC
output2
2021-02-07-0448-04S.JKH_RR
2021-02-07-0448-04S.JKH_RR_BHE
output3
2021-02-07-0448-04S.JKH_RR_BHE-BHR.SAC
2021-02-07-0448-04S.JKH_RR-BHT.SAC

Bash script with multiline heredoc doesn't output anything

I'm writing a script to send SQL output in mail, but it is not executing successfully and is not generating the output I want.
The query generates two columns with multiple rows. How can I generate the output in table format as below?
Below is my code:
#!/bin/bash
ORACLE_HOME= **PATH
export ORACLE_HOME
PATH=$PATH:$ORACLE_HOME/bin
export PATH
TNS_ADMIN= ** PATH
export TNS_ADMIN
today=$(date +%d-%m-%Y)
output=$(sqlplus -S user/pass#service <<EOF
set heading off;
SELECT distinct list_name ,max(captured_dttm) as Last_received FROM db.table1
group by list_name having max(captured_dttm) <= trunc(sysdate - interval '2' hour);
EOF)
if [ -z "$output" ];
then
echo"its fine"
exit
else
echo "
Dear All,
Kindly check we've not received the list for last 2 hour : $output
Regards,
Team" | mailx -S smtp=XX.XX.X.XX:XX -s "URGENT! Please check list FOR $today" user#abc.com
fi
When using a here document, the closing string can't be followed by anything but a newline. Move the closing parenthesis to the next line:
output=$(sqlplus -S user/pass#service <<EOF
...
EOF
)

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.

Bash script Output to file fails from script but works from bash

I have this script that checks if the dcos auth login works, but the file i am redirecting the output to is always zero size, when i run the script from bash shell the file is greater than zero . what am i doing wrong ?? , the two functions i use below:
try_to_login()
{
# first needs to be logged as skyusr
# try to login and log the result to tmp file
# Sometimes the file is empty so we try again to login
# if the second time is OK it jumps to check the output
cd /home/skyusr/scripts/
dcos auth login --username=admin --password=admin > /home/skyusr/scripts/tmp.sal
}
check_login_result()
{
# Checks if the output of the login is "Login Successful!"
# If YES then writes to log file, if not sends mail and writes to log.
#export mail_to="salim.bisharat#amdocs.com,anis.faraj#amdocs.com"
export mail_to="salim.bisharat#amdocs.com"
now=$(date)
text_to_check=$(cat /home/skyusr/scripts/tmp.sal)
if [ -s /home/skyusr/scripts/tmp.sal ]
then
if [ "$text_to_check" = "Login successful!" ]
then
echo "$now - Check Successful" >> /home/skyusr/scripts/logs/login_log.log
else
cat /home/skyusr/scripts/logs/mail_temp.log | mailx -s "!!! CRITITCAL -- Check DCOS login !!!" $mail_to
echo "$now - !! ERROR ! Sent mail !! " >> /home/skyusr/scripts/logs/login_log.log
fi
fi
}
In this script you define, but you do not call the functions. Simply append function calls:
# ...
echo "$now - !! ERROR ! Sent mail !! " >> /home/skyusr/scripts/logs/login_log.log
fi
fi
} # ... the last line of your script here
try_to_login # calls here ...
check_login_result

Resources