"WRITE" command works manually but not via script - bash

My Co-Workers and I use the screen program on our Linux JUMP server to utilize as much screen space as possible. With that, we have multiple screens setup so that messages can go to one while we do work in another.
With that, i have a script that is used to verify network device connectivity which will send messages to my co-workers regardless if there is anything down or not.
The script initially references a file with their usernames in it and then grabs the highest PTS number which denotes the last screen session they activated and then puts it into the proper format in an external file like such:
cat ./netops_techs | while read -r line; do
temp=$(echo $line)
temp2=$(who | grep $temp | sed 's/[^0-9]*//g' | sort -n -r | head -n1)
if who | grep $temp; then
echo "$temp pts/$temp2" >> ./tech_send
fi
done
Once it is done, it will then scan our network every 5 minutes and send updates to the folks in the file "./tech_send" like such:
Techs=$(cat ./tech_send)
if [ ! -f ./Failed.log ]; then
echo -e "\nNo network devices down at this time."
for d in $Techs
do
cat ./no-down | write $d
done
else
# Writes downed buildings localy to my terminal
echo -e "\nThe following devices are currently down:"
echo ""
echo "IP Hostname Model Building Room Rack Users Affected" > temp_down.log
grep -f <(sed 's/.*/\^&\\>/' Failed.log) Asset-Location >> temp_down.log
cat temp_down.log | column -t > Down.log
cat Down.log
# This will send the downed buildings to the rest of NetOps
for d in $Techs
do
cat Down.log | write $d
done
fi
The issue stems from, when they are working in their main sectioned screen, the messages will pop up in that active screen instead of the inactive screen. If I send them a message manually such as:
write jsmith pts/25
Test Test
and then CTRL+D, it works fine even if they are in a different session. Via script though, it gives an error stating that:
write: jsmith is logged in more than once; writing to pts/23
write: jsmith/pts/25 is not logged in
I have verified the "tech_send" file and it has the correct format for them:
jsmith pts/25
Would appreciate any insight on why this is happening.

Related

How to delete a connection by type with nmcli?

I want to have a bash script that can delete all my network manager connections of type gsm with nmcli.
What is the best approach for this?
This is actually a trickier question than it seems on the surface, because NetworkManager allows for connection names with spaces in them. This makes programmatic parsing of the output of nmcli connection show for connection names a bit awkward. I think the best option for scripting would be to rely on the UUID, since it seems to consistently be a 36 character group of hexidecimal characters and dashes. This means we can pull it consistently with a regular expression. So for example you could get a list of the UUIDs for gsm connections with the following:
$ nmcli connection show | grep gsm | grep -E -o '[0-9a-f\-]{36}'
cc823da6-d4e1-4757-a37a-aaaaaaaaa
etc
So you could grab the UUIDs and then delete based on the UUID:
GSM_UUIDS=$(nmcli connection show | grep gsm | grep -E -o '[0-9a-f\-]{36}')
while IFS= read -r UUID; do echo nmcli connection delete $UUID; done <<< "$GSM_UUIDS"
Run with the echo to make sure you're getting the result you expect, then you can remove it and you should be in business. I ran locally with some dummy GSM connections and it seemed to work they way you would want it to:
GSM_UUIDS=$(nmcli connection show | grep gsm | grep -E -o '[0-9a-f\-]{36}')
while IFS= read -r UUID; do nmcli connection delete $UUID; done <<< "$GSM_UUIDS"
Connection 'gsm' (cd311376-d7ab-4891-ba73-e4e8a3fc6614) successfully deleted.
Connection 'gsm-1' (54171181-5c37-4224-baf5-9eb36458f773) successfully deleted.
nmcli con del $(nmcli -t -f UUID,TYPE con | awk -F":" '{if ($2 == "gsm") print $1}')

How to compare the last words of the lines in a file

I'm using a Raspberry Pi as a backup server. I use cron to run each backup job nightly and log the output to a file specific to each job. So each morning I have a bunch of log files (job1.log .. jobN.log). The log files are overwritten each time the job runs. I have another cron job (that runs after all the backup jobs) that sends me an email showing the last line of each log file. This all works as expected.
I'd like to be able to get a status in the subject of the email based on the last lines of the log files. When a backup job is successfully completed, the last line of the log file has some info followed by the word "completed" (which isn't included if the job fails). In my script that sends the email, I use "tail -1 >> summary.txt" for each log file, so summary.txt is a collection containing the last line of each logfile (and is included in the body of the email sent to me).
What I'd like to do is to check the last word of each line in summary.txt to see if all jobs completed successfully, and set the subject of the email appropriately (a simple "backup succeeded" or "backup failed" would be sufficient).
What would be the best way to do this? I know one possibility would be to use awk '{print $NF}' to get the last word of each line, but I'm not sure how to use that.
EDIT: As requested, here is the simplified code I'm currently using to send the "status" email to myself:
#!/bin/sh
tail -1 job1.log > summary.txt
tail -1 job2.log >> summary.txt
tail -1 job3.log >> summary.txt
mail -s "PI Backup Report" myemail#myhost < summary.txt
I know I could create an additional file with just the last lines by adding
awk '{print $NF}' summary.txt > results.txt
to the above script before the "mail" line, but then I still need to parse the results.txt file. How would I determine the status based on that file? Thanks again!
Measure total vs success lines in summary.txt.
xargs echo to trim results of excess whitespace
grep with regex specifying the line should end in "completed"
wc -l for line count
Set the title using an if statement
TOTAL=$(wc -l < summary.txt | xargs echo)
SUCCESS=$(grep -e 'completed$' summary.txt | wc -l)
title=$(if [ $TOTAL = $SUCCESS ]; then echo 'All Succeeded'; else echo "$SUCCESS/$TOTAL succeeded"; fi)
echo $title # or pass into mail command as subject

Kill a database query after command outputs a pattern

I have the following command that downloads a file from a database, then checks its integrity and exits:
cmd_db -get file_1
ouput:
Getting file_1
Transferring data (10% done)
Transferring data (20% done)
...
Transferring data (90% done)
Transferring data (100% done)
Getting MD5
ERROR: stream broke
Exiting...
The file downloads properly but is erased after the error occurs.
I managed to manually kill the command when the output reaches "100% done".
After checking the MD5 manually, the file is actually fine.
There is something wrong with my connection and I already spent days trying to figure it out. So far the manual killing is the only solution that works.
So, I am trying to automate the killing but none of the following commands work. The program actually stops producing an output when it reaches "100% done", but continues until the end and erases the file.
cmd_db -get file_1 | sed '/100% done/q'
cmd_db -get file_1 | while read line; do test "$line" = "100% done" && killall; done
until cmd_db -get file_1 | grep -m 1 "100% done"; do : ; done
Is there any other way to do that?
Alright, since I don't know what your command is, I'll have to work around it. First, redirect the output of the command to grep, which will will look for the '100% done' and put that in a file. Put the whole thing in the background:
cmd_db -get file_1 | grep '100% done' > tmp$$ &
You are going to need that process ID, so that we can kill it later:
child=$!
Now, that file is going to be empty until we get that string. Put yourself into an infinite loop, and test the file size until it's greater than zero, then kill the child process and break out of the loop:
while true; do
[ $( wc -c tmp$$ | cut -d' ' -f1 ) -gt 0 ] && { kill -9 $child; break; }
done
Good luck!

determine if connection is wired or wireless?

I have a bash script I made which when run on each of my computers, detects the number of CPU cores, HDDs/partitions, battery present or not, etc, and generate a conkyrc file to display the relevant info for that PC using the style I prefer in my conky. I am having difficulty determining whether the PC is on a wired or wireless internet connection however.
Does anyone know a way to determine the type of connection with a bash script?
Try this:
tail -n+3 /proc/net/wireless | grep -q . && echo "We are wireless"
Details
On a hardwired system, the contents of /proc/net/wireless consist of two header lines:
# cat /proc/net/wireless
Inter-| sta-| Quality | Discarded packets | Missed | WE
face | tus | link level noise | nwid crypt frag retry misc | beacon | 22
On a system with an active wireless interface, there will be a third line displaying data about that interface.
The command above works as follows
The tail -n+3 command is used to remove the header.
The grep -q . command tests for the presence of subsequent lines that are present if a wireless interface is active.
Alternative
iwconfig is a utility that reads information from /proc/net/wireless:
iwconfig 2>&1 | grep -q ESSID && echo "We are wireless"

Connecting Two Bash Commands

I have Ubuntu Linux. I found one command will let me download unread message subjects from Gmail:
curl -u USERNAME:PASSWORD --silent "https://mail.google.com/mail/feed/atom" | tr -d '\n' | awk -F '<entry>' '{for (i=2; i<=NF; i++) {print $i}}' | sed -n "s/<title>\(.*\)<\/title.*name>\(.*\)<\/name>.*/\2 - \1/p"
...and then another command to let me send mail easily (once I installed the sendemail command via apt-get):
sendEmail -f EMAIL#DOMAIN.COM -v -t PHONE#SMS.COM -u Gmail Notifier -m test -s MAILSERVER:PORT -xu EMAIL#DOMAIN.COM -xp PASSWORD
(Note when in production I'll probably swap -v above with -q.)
So, if one command downloads one line subjects, how can I pipe these into the sendEmail command?
For instance, I tried using a pipe character between the two, where I used "$1" after the -m parameter, but what happened was that when I had no unread emails it would still send me at least one empty message.
If you help me with this, I'll use this information to share on StackOverflow how to build a Gmail Notifier that one can hook up to SMS messages on their phone.
I think if you mix viraptor & DigitalRoss' answers you get what you want. I created a sample test by creating a fake file with the following input:
File contents:
foo
bar
baz
Then I ran this command:
% cat ~/tmp/baz | while read x; do if [[ $x != "" ]]; then echo "x: '$x'"; fi; done
This will only print lines with input out. I'm not familiar with sendEmail; does it need the body to be on stdin or can you pass it on the cmdline?
You do know you can do that directly in gmail by using a filter and your SMS email gateway, right?
But back to the question...
You can get control in a shell script for command output with the following design pattern:
command1 | while read a b c restofline; do
: execute commands here
: command2
done
Read puts the first word in a, the second in b, and the rest of the line in restofline. If the loop consists of only a single command, the xargs program will probably just do what you want. Read in particular about the -I parameter which allows you to place the substituted argument anywhere in the command.
Sometimes the loop looks like ... | while read x; do, which puts the entire line into x.
Try this structure:
while read line
do
sendemailcommand ... -m $line ...
done < <(curlcommand)
I'd look at the xargs command, which provides all the features you need (as far as I can tell).
http://unixhelp.ed.ac.uk/CGI/man-cgi?xargs
Maybe something like this:
curl_command > some_file
if [[ `wc -l some_file` != "0 some_file" ]] ; then
email_command < some_file
fi

Resources