a bash script with a yad window and checking internet connection - bash

I'm working on a bash script, opening a yad window and keeping it opened until internet connection is present and then closing it.
It looks as follow:
#!/bin/bash
yad_wind1_id=""
yad_wind1_id_txt_str=""
NET_STATE=0
function test_net_connection()
{
(ping -c 1 www.google.com &>/dev/null)
XCODE=$?
if [ $XCODE == 0 ]; then
NET_STATE=1
else
NET_STATE=0
fi
}
function kill_yad_proc()
{
kill_proc=$(ps aux | grep "$1" | grep -v "grep $1" | \
awk '{print $2}' | sed 's:^:kill :')
eval $kill_proc
}
function yad_proc_id_string()
{
yad_win_id=`echo $(date +%F-%H-%M-%6N) | \
sed 's/-/_/g' | sed 's/^/text=/'`
yad_win_id_txt_str="--""$yad_win_id"
}
yad_proc_id_string
# ...and here starts my problem...
while [[ $NET_STATE == 0 ]]; do
test_net_connection
if [[ $XCODE == 0 ]]; then
NET_STATE=1
kill_yad_proc "$yad_win_id"
break
exit
else
NET_STATE=0
continue
fi
done | `yad --fixed --skip-taskbar --undecorated \
"$yad_win_id_txt_str" \
--text="Waiting for internet connection..." \
--button='Quit:bash -c "kill -USR1 $YAD_PID"' \
--image=disconnected.png`
In this version script works just partly: when internet is off, yad window appears and disappears when internet is on (ping returns 0). Unfortunately, when I press [Quit] button or [escape] (however I'd like to keep this option), it doesn't work as I'd like it to.
What should I add/change? Where the mistake is done?

"when I press [Quit] button or [escape] (however I'd like to keep this
option)"
I guess yad is outside loop and this is your problem.
TIP.
- I do not see sense to check the connection with ping.
But maybe you need and maybe this is best way.
Best should be find file inside system for internet conection status and use inotifywait
however inotify sometimes is also buggy so you should be carefully.
- You can add own icon tray with yad,
but you need create two functions with yad with other icons and title.
When inetrnet status will change, you can kill first yad window and open next window.
This can be in "while" loop when status internet is tested.

Related

How to detect a non-rolling log file and pattern match in a shell script which is using tail, while, read, and?

I am monitoring a log file and if PATTERN didn't appear in it within THRESHOLD seconds, the script should print "error", otherwise, it should print "clear". The script is working fine, but only if the log is rolling.
I've tried reading 'timeout' but didn't work.
log_file=/tmp/app.log
threshold=120
tail -Fn0 ${log_file} | \
while read line ; do
echo "${line}" | awk '/PATTERN/ { system("touch pattern.tmp") }'
code to calculate how long ago pattern.tmp touched and same is assigned to DIFF
if [ ${diff} -gt ${threshold} ]; then
echo "Error"
else
echo "Clear"
done
It is working as expected only when there is 'any' line printed in the app.log.
If the application got hung for any reason and the log stopped rolling, there won't be any output by the script.
Is there a way to detect the 'no output' of tail and do some command at that time?
It looks like the problem you're having is that the timing calculations inside your while loop never get a chance to run when read is blocking on input. In that case, you can pipe the tail output into a while true loop, inside of which you can do if read -t $timeout:
log_file=/tmp/app.log
threshold=120
timeout=10
tail -Fn0 "$log_file" | while true; do
if read -t $timeout line; then
echo "${line}" | awk '/PATTERN/ { system("touch pattern.tmp") }'
fi
# code to calculate how long ago pattern.tmp touched and same is assigned to diff
if [ ${diff} -gt ${threshold} ]; then
echo "Error"
else
echo "Clear"
fi
done
As Ed Morton pointed out, all caps variable names are not a good idea in bash scripts, so I used lowercase variable names.
How about something simple like:
sleep "$threshold"
grep -q 'PATTERN' "$log_file" && { echo "Clear"; exit; }
echo "Error"
If that's not all you need then edit your question to clarify your requirements. Don't use all upper case for non exported shell variable names btw - google it.
To build further on your idea, it might be beneficial to run the awk part in the background and a continuous loop to do the checking.
#!/usr/bin/env bash
log_file="log.txt"
# threshold in seconds
threshold=10
# run the following process in the background
stdbuf -oL tail -f0n "$log_file" \
| awk '/PATTERN/{system("touch "pattern.tmp") }' &
while true; do
match=$(find . -type f -iname "pattern.tmp" -newermt "-${threshold} seconds")
if [[ -z "${match}" ]]; then
echo "Error"
else
echo "Clear"
fi
done
This looks to me like a watchdog timer. I've implemented something like this by forcing a background process to update my log, so I don't have to worry about read -t. Here's a working example:
#!/usr/bin/env bash
threshold=10
grain=2
errorstate=0
while sleep "$grain"; do
date '+[%F %T] watchdog timer' >> log
done &
trap "kill -HUP $!" 0 HUP INT QUIT TRAP ABRT TERM
printf -v lastseen '%(%s)T'
tail -F log | while read line; do
printf -v now '%(%s)T'
if (( now - lastseen > threshold )); then
echo "ERROR"
errorstate=1
else
if (( errorstate )); then
echo "Recovered, yay"
errorstate=0
fi
fi
if [[ $line =~ .*PATTERN.* ]]; then
lastseen=$now
fi
done
Run this in one window, wait $threshold seconds for it to trigger, then in another window echo PATTERN >> log to see the recovery.
While this can be made as granular as you like (I've set it to 2 seconds in the example), it does pollute your log file.
Oh, and note that printf '%(%s)T' format requires bash version 4 or above.

Code in Shell Script runs before other Operations

This same problem is better formulated in a question posted to the Unix & Linux StackExchange community.
I am programming a script which opens on a key press, open a new terminal (gnome-terminal), runs scrot (screenshot tool), saves the picture with random name to a directory, uploads it to pomf.cat and copies the link to the clipboard.
This works fine. What im trying to do now is, when uploading is done, close the terminal.
My script works like this:
Shortcut (PrtScr) -> gnome-terminal -e "python path/to/script.py" -> Start Scrot -> Save File (and remember path to file) -> bash script2.sh path/to/picture -> Upload to pomf.cat -> Get the link -> Put into clipboard via "xclip -selection clipboard"
Since i want to close the Terminal after putting the String into Clipboard, i added this:
eval $(printf $link | xclip -selection clipboard && sleep 1 && pkill terminal)
The problem with this is, nothing gets copied into clipboard and the terminal closes.
However, without "&& sleep 1 && pkill terminal" the link gets copied but the terminal stays open.
Thanks in advance.
//EDIT
First Script (for running scrot)
#!/usr/bin/env python
import os
import uuid
import time
def rstring(string_length=10):
random = str(uuid.uuid4())
random = random.upper()
random = random.replace("-","")
return random[0:string_length]
randomString = rstring(16)
os.system("scrot -s -q 100 /home/timon/screenshots/" + randomString + ".jpg")
while True:
processRead = os.popen("ps aux | grep \"scrot -s\" | cat").read()
if "scrot -s" not in processRead:
time.sleep(1)
else:
break
system.sleep(3)
os.system("/home/timon/.screenshot_stuff/./screen.sh /home/timon/screenshots/" + randomString + ".jpg")
Second Script (for uploading the screenshot)
#!/usr/bin/env bash
dest_url='https://cuntflaps.me/upload.php'
return_url='https://a.cuntflaps.me'
if [[ -n "${1}" ]]; then
file="${1}"
if [ -f "${file}" ]; then
printf "Uploading ${file}..."
my_output=$(curl --silent -sf -F files[]="#${file}" "${dest_url}")
n=0 # Multipe tries
while [[ $n -le 3 ]]; do
printf "try #${n}...\n"
if [[ true ]]; then
return_file=$(echo "$my_output" | grep "url" | sed 's/\,//g' | sed 's/\\//g' | sed 's/\"//g' | sed 's/\url://g' | tr -d ' ')
printf 'done.\n'
break
else
printf 'failed.\n'
((n = n +1))
fi
done
printf "$return_file" | xclip -selection clipboard && pkill terminal
else
printf 'Error! File does not exist!\n'
exit 1
fi
else
printf 'Error! You must supply a filename to upload!\n'
exit 1
fi
So in the end i came up with my own solution.
The problem seemed to be xclip itself.
Now i use "xsel --clipboard --input", which seems to work, even after exiting directly.

I'd like to grep the output of ifconfig in bash

I'm making a small script to determine if I have an internet connection on OSX. More of just practice, I suppose.
In terminal "ifconfig | grep -cs 'status: active' " will return 1 if there's at least one active connection
The script I have is this
#!/bin/bash
detect(){
ONLINE=ifconfig | grep -cs 'status: active'
}
if [[ detect = 1 ]]
then
echo "Online"
else
echo "Offline"
fi
However the Variable ONLINE always returns 0.
From what I can tell/understand, this has to do with using a pipe inside of the script. A sub-pipe is used when running the command, and ONLINE just gets stuck with 0 as the sub-pipe closes.
I think I see the issue, but I don't know how to get around this. I saw a bunch of work arounds for scripts having this issue with while loops, but nothing where I need the output from ifconfig fed into grep.
Several problems with your current script:
You set a variable ONLINE, but you test for detect.
You don't actually assign the result of the ifconfig | grep -cs 'status: active' command to the variable ONLINE
You use = instead of == to test for equality
The following would seem to be closer to what you intended:
#!/bin/bash
ONLINE=$(ifconfig | grep -cs 'status: active')
if [[ $ONLINE == 1 ]]
then
echo "online"
else
echo "offline"
fi
use this:
ONLINE=$(ifconfig | grep -cs 'status: active')
cause without "$" what bash will return is the result of the command being successful or not and if it is successful it is always zero.
Or you can just keep it simple like this ...
ifconfig | grep 'status: active' > /dev/null 2>&1
if [ $? == 0 ]
then
echo "online"
else
echo "offline"
fi
As none of the answers explain the exact issue with your script, I'm adding an answer.
The issue lies with the line ONLINE=ifconfig | grep -cs 'status: active'.
What is wrong is that there is no command substitution ($(...) or `...`) used in the line. As correctly suggested by other answers as well, the assignment needs to be $(ONLINE=ifconfig | grep -cs 'status: active').
What this line actually does is that it assigns the string "ifconfig" to the variable ONLINE and pipes the output of that (no output in this case) through grep -cs ...
One point to note is that this assignment is only for the duration of that line, and does not survive till the next line. To illustrate:
samveen#precise:~$ I=0
samveen#precise:~$ echo $I
0
samveen#precise:~$ I=1 | echo "blank"
blank
samveen#precise:~$ echo $I
0
Edit: I totally missed another very important point: Subroutines do not return values in bash, just exit status
Thus capturing variables from subroutine calls needs the subroutine to echo it's expected return value and it's call needs an assignment with command substitution.
Something like this:
detect(){
ONLINE=$(ifconfig | grep -cs 'status: active')
echo $ONLINE
}
if [[ $(detect) -eq 1 ]]
then
echo "Online"
else
echo "Offline"
fi
Also, use -eq to test numeric equality.
Finally, the shortest way to do what you want is
ifconfig | grep -q 'status:active' && echo "online" || echo "offline"

Loop shell script until successful log message

I am trying to get a shell script to recognize when an app instance has come up. That way it can continue issuing commands.
I've been thinking it would be something like this:
#/bin/bash
startApp.sh
while [ `tail -f server.log` -ne 'regex line indicating success' ]
do
sleep 5
done
echo "App up"
But, even if this worked, it wouldn't address some concerns:
What if the app doesn't come up, how long will it wait
What if there is an error when bringing the app up
How can I capture the log line and echo it
Am I close, or is there a better way? I imagine this is something that other admins have had to overcome.
EDIT:
I found this on super user
https://superuser.com/questions/270529/monitoring-a-file-until-a-string-is-found
tail -f logfile.log | while read LOGLINE
do
[[ "${LOGLINE}" == *"Server Started"* ]] && pkill -P $$ tail
done
My only problem with this is that it might never exit. Is there a way to add in a maximum time?
Ok the first answer was close, but didn't account for everything I thought could happen.
I adapted the code from this link:
Ending tail -f started in a shell script
Here's what I came up with:
#!/bin/bash
instanceDir="/usr/username/server.name"
serverLogFile="$instanceDir/server/app/log/server.log"
function stopServer() {
touch ${serverLogFile}
# 3 minute timeout.
sleep 180 &
local timerPid=$!
tail -n0 -F --pid=${timerPid} ${serverLogFile} | while read line
do
if echo ${line} | grep -q "Shutdown complete"; then
echo 'Server Stopped'
# stop the timer..
kill ${timerPid} > /dev/null 2>&1
fi
done &
echo "Stoping Server."
$instanceDir/bin/stopserver.sh > /dev/null 2>&1
# wait for the timer to expire (or be killed)
wait %sleep
}
function startServer() {
touch ${serverLogFile}
# 3 minute timeout.
sleep 180 &
local timerPid=$!
tail -n0 -F --pid=${timerPid} ${serverLogFile} | while read line
do
if echo ${line} | grep -q "server start complete"; then
echo 'Server Started'
# stop the timer..
kill ${timerPid} > /dev/null 2>&1
fi
done &
echo "Starting Server."
$instanceDir/bin/startserver.sh > /dev/null 2>&1 &
# wait for the timer to expire (or be killed)
wait %sleep
}
stopServer
startServer
Well, tail -f won't ever exit, so that's not what you want.
numLines=10
timeToSleep=5
until tail -n $numLines server.log | grep -q "$serverStartedPattern"; do
sleep $timeToSleep
done
Be sure that $numLines is greater than the number of lines that might show up during $timeToSleep when the server has come up.
This will continue forever; if you want to only allow so much time, you could put a cap on the number of loop iterations with something like this:
let maxLoops=60 numLines=10 timeToSleep=5 success=0
for (( try=0; try < maxLoops; ++try )); do
if tail -n $numLines server.log | grep -q "$serverStartedPattern"; then
echo "Server started!"
success=1
break
fi
sleep $timeToSleep
done
if (( success )); then
echo "Server started!"
else
echo "Server never started!"
fi
exit $(( 1-success ))

How to make "if not true condition"?

I would like to have the echo command executed when cat /etc/passwd | grep "sysa" is not true.
What am I doing wrong?
if ! [ $(cat /etc/passwd | grep "sysa") ]; then
echo "ERROR - The user sysa could not be looked up"
exit 2
fi
try
if ! grep -q sysa /etc/passwd ; then
grep returns true if it finds the search target, and false if it doesn't.
So NOT false (! false) == true.
if evaluation in shells are designed to be very flexible, and many times doesn't require chains of commands (as you have written).
Also, looking at your code as is, your use of the $( ... ) form of cmd-substitution is to be commended, but think about what is coming out of the process. Try echo $(cat /etc/passwd | grep "sysa") to see what I mean. You can take that further by using the -c (count) option to grep and then do if ! [ $(grep -c "sysa" /etc/passwd) -eq 0 ] ; then which works but is rather old school.
BUT, you could use the newest shell features (arithmetic evaluation) like
if ! (( $(grep -c "sysa" /etc/passwd) == 0 )) ; then ...`
which also gives you the benefit of using the c-lang based comparison operators, ==,<,>,>=,<=,% and maybe a few others.
In this case, per a comment by Orwellophile, the arithmetic evaluation can be pared down even further, like
if ! (( $(grep -c "sysa" /etc/passwd) )) ; then ....
OR
if (( ! $(grep -c "sysa" /etc/passwd) )) ; then ....
Finally, there is an award called the Useless Use of Cat (UUOC). :-) Some people will jump up and down and cry gothca! I'll just say that grep can take a file name on its cmd-line, so why invoke extra processes and pipe constructions when you don't have to? ;-)
I hope this helps.
I think it can be simplified into:
grep sysa /etc/passwd || {
echo "ERROR - The user sysa could not be looked up"
exit 2
}
or in a single command line
$ grep sysa /etc/passwd || { echo "ERROR - The user sysa could not be looked up"; exit 2; }
This one
if [[ ! $(cat /etc/passwd | grep "sysa") ]]; then
echo " something"
exit 2
fi
What am I doing wrong?
$(...) holds the value, not the exit status, that is why this approach is wrong. However, in this specific case, it does indeed work because sysa will be printed which makes the test statement come true. However, if ! [ $(true) ]; then echo false; fi would always print false because the true command does not write anything to stdout (even though the exit code is 0). That is why it needs to be rephrased to if ! grep ...; then.
An alternative would be cat /etc/passwd | grep "sysa" || echo error. Edit: As Alex pointed out, cat is useless here: grep "sysa" /etc/passwd || echo error.
Found the other answers rather confusing, hope this helps someone.
Here is an answer by way of example:
In order to make sure data loggers are online a cron script runs every 15 minutes that looks like this:
#!/bin/bash
#
if ! ping -c 1 SOLAR &>/dev/null
then
echo "SUBJECT: SOLAR is not responding to ping" | ssmtp abc#def.com
echo "SOLAR is not responding to ping" | ssmtp 4151112222#txt.att.com
else
echo "SOLAR is up"
fi
#
if ! ping -c 1 OUTSIDE &>/dev/null
then
echo "SUBJECT: OUTSIDE is not responding to ping" | ssmtp abc#def.com
echo "OUTSIDE is not responding to ping" | ssmtp 4151112222#txt.att.com
else
echo "OUTSIDE is up"
fi
#
...and so on for each data logger that you can see in the montage
at http://www.SDsolarBlog.com/montage
FYI, using &>/dev/null redirects all output from the command, including errors, to /dev/null
(The conditional only requires the exit status of the ping command)
Also FYI, note that since cron jobs run as root there is no need to use sudo ping in a cron script.
simply:
if ! examplecommand arg1 arg2 ...; then
#code block
fi
without any brackets.
On Unix systems that supports it (not macOS it seems):
if getent passwd "$username" >/dev/null; then
printf 'User %s exists\n' "$username"
else
printf 'User %s does not exist\n' "$username"
fi
This has the advantage that it will query any directory service that may be in use (YP/NIS or LDAP etc.) and the local password database file.
The issue with grep -q "$username" /etc/passwd is that it will give a false positive when there is no such user, but something else matches the pattern. This could happen if there is a partial or exact match somewhere else in the file.
For example, in my passwd file, there is a line saying
build:*:21:21:base and xenocara build:/var/empty:/bin/ksh
This would provoke a valid match on things like cara and enoc etc., even though there are no such users on my system.
For a grep solution to be correct, you will need to properly parse the /etc/passwd file:
if cut -d ':' -f 1 /etc/passwd | grep -qxF "$username"; then
# found
else
# not found
fi
... or any other similar test against the first of the :-delimited fields.
I'd expect to see in the answers the direct use of grep with -q option (as we don't care the result but need only the return code.)
if ! grep -qs "sysa" /etc/passwd; then
echo "ERROR - The user sysa could not be looked up" >&2
exit 2
fi
or (to use chained execution on fail)
grep -qs "sysa" /etc/passwd || {
echo "ERROR - The user sysa could not be looked up" >&2
exit 2
}
Better, because the opposite is wanted, there's option -v for that
if grep -qsv "sysa" /etc/passwd; then
echo "ERROR - The user sysa could not be looked up" >&2
exit 2
fi
or (to use chained execution on success)
grep -qsv "sysa" /etc/passwd && {
echo "ERROR - The user sysa could not be looked up" >&2
exit 2
}
Notes
I like redirecting error messages to stderr, but echo output to stdout, hence >&2
Taylor the search pattern, e.g something like '^sysa:' if it's full login.

Resources