Text output from a SSH connection to a Mikrotik appliance cannot be properly read by bash? - bash

I'm writing a simple script to notify me whenever a new firmware update is available in my Mikrotik router. The RouterOS command for this purpose is:
ssh myrouter system package update print
And when its run normally it works fine, showing something like this:
channel: stable
installed-version: 6.48
latest-version: 6.48.1
status: New version is available
The full command I am using to only retrieve the relevant info so a notification is triggered is:
ssh -q myrouter system package update print |tail -2 |head -1 |sed "s/: /\n/" |tail -1
Which results in this when it is run "as is" (not inside a script nor anything else):
New version is available
However, I cannot seem to be able to parse this text inside a simple if statement, no matter if I manipulate it directly or first redirecting it to a local file:
UPDATES_AVAILABLE=$(ssh myrouter system package update print |tail -2 |head -1 |cut -d: -f2)
if [[ "$(echo ${UPDATES_AVAILABLE})" == "New version is available" ]]; then
echo "INFO: New firmware version is available"
fi
This last one always returns no content, wether is run inside a script or directly in the shell.
What am I missing?

Oh nevermind, I just fixed it.
After redirecting the output to a /tmp/mikrotik.tmp file I just thought about running:
file /tmp/mikrotik.tmp
And then I saw that it returned this:
mikrotik: ASCII text, with CRLF line terminators
So as soon as I added | dos2unix inside my string filter command it just started working as expected, and now this:
UPDATES_AVAILABLE=$(ssh myrouter system package update print |dos2unix |tail -2 |head -1 |cut -d: -f2)
if [[ "$(echo ${UPDATES_AVAILABLE})" == "New version is available" ]]; then
echo "INFO: New firmware version is available"
fi
Is returning correctly:
INFO: New firmware version is available

Related

When I pipe the service --status-all command to grep, why do extra lines show up in the output?

For example,
sudo service --status-all | grep -oE 'php[0-9]+.[0-9]+'
generates the following output.
[ ? ] hwclock.sh
[ ? ] networking
php7.0
php7.3
My goal is to extract the version of another software package and put it into a configuration script so that the script won't break if that package gets upgraded or downgraded. If my understanding of regular expressions and the piping operator is correct, the first two lines shouldn't even show up in the output.
Can anyone explain to me why this is happening?
Redirecting the output to stderr eliminated the unwanted lines. I also piped the output to tail -1 to get the last line only--the one with the latest version.
sudo service --status-all 2>/dev/null | grep -oE 'php[0-9]+.[0-9]+' | tail -1

"WRITE" command works manually but not via script

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.

'lsb_release -a' prints an additional line while using it in bash script

I am writing a bash script which has a subtask of printing the Ubuntu name on which the system is running.
I did:
ubuntu_ver=$(lsb_release -a | grep Description | cut -d":" -f2)
echo $ubuntu_ver
Ubuntu 12.04 LTS got stored in variable ubuntu_ver but and additional line got printed on the top. The output was:
No LSB modules are available.
Ubuntu 12.04.4 LTS
How to prevent the 'No LSB modules...' from getting printed?
In fact this line gets printed whenever I try to store something in a variable from the command 'lsb_release -a'.
In fact this line gets printed whenever I try to store something in a
variable from the command lsb_release -a
This suggests that the offending line is actually an error message. Discard it:
ubuntu_ver=$(lsb_release -a 2>/dev/null | grep Description | cut -d":" -f2)
-a includes -v which returns a list of lsb modules, which is why you get the message that none are found.
Also I'd suggest just asking for the description (-d) and omitting the header (-s):
ubuntu_ver=$(lsb_release -ds)
echo $ubuntu_ver

Is there a way to click a link in Firefox and open a file in an existing VIM session?

I know it's possible to open links in an html page (let's say, if you're using Firefox) with TextMate if the link has this format:
View
But is it possible to do a similar thing with VIM? Perhaps like so:
View
Ideally this would use an existing VIM session.
Cheers,
Bernie
Found a way to do it:
Add a Protocol handler to Firefox
Open firefox and navigate to about:config
Add the following keys
network.protocol-handler.warn-external.txmt boolean false
network.protocol-handler.external.txmt boolean true
#the last one is the path to the script we're about to create
network.protocol-handler.app.txmt string ~/protocol_handler/prot.sh
# I ended up needing this one as well on another machine, (no idea why)
network.protocol-handler.expose.txmt boolean false
Create the script ~/protocol_handler/prot.sh
Copy and paste the following into the file:
#! /usr/bin/env ruby
file_result = ARGV[0].scan(/file\:\/\/((\w|\/|\.)*).*/)
file_path = file_result[0][0]
line_result = ARGV[0].scan(/\&amp\;line\=(\d*).*/)
if line_result
line = line_result[0][0]
system "gvim --remote-silent +#{line} #{file_path}"
else
system "gvim --remote-silent #{file_path}"
end
Save the file.
Change the file mode to be executable:
$ chmod +x ~/protocol_handler/prot.sh
I'm not sure if you have to restart Firefox or not.
If you actually want to use the "vim://" protocol just change the ending on the network keys from txmt to vim. Since several Rails plugins (rails-footer, namely) out there already use txmt, I just used that to avoid recoding.
Have fun!
Berns
http://www.mozilla.org/projects/netlib/new-handler.html
To get tmxt:// links working with gedit, I had to use a bash script from #Rystraum's related answer instead of the Ruby, ~/bin/txmt_proto.bash:
#!/bin/bash
FILE=$1
FILE=$(echo $FILE | grep -o "file:/\/.\+" | cut -c 8- | sed -e 's/%2F/\//g')
LINE=$(echo $FILE | grep -o "\&line=[0-9]\+")
LINE=$(echo $LINE | grep -o "[0-9]\+")
FILE=$(echo $FILE | grep -o "\(.\+\)\&")
FILE=$(echo $FILE | cut -d'&' -f1)
gedit +$LINE $FILE
and change the Firefox config network.protocol-handler.app.txmt to point at the script:
network.protocol-handler.app.txmt string ~/bin/txmt_proto.bash

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