bash script to trigger on and off events from journalctl - bash

I am trying to write a bash script to set a variable "status" to either online or offline.
I use logger "trigger offline" and logger "trigger online" for simplicity.
The offline event triggers ok, and I can see the output correct in journalctl, but when the online event is sent by logger, the variable £status is still offline. If its offine, I want it to restart the NetworkManger until the event online is triggered. thanks
!/bin/bash
journalctl -fqn0 | \
while read line
do
#trigger if offline
echo "$line" | grep "trigger offline"
if [ $? = 0 ]
then
status="offline"
fi
#trigger if online
echo "$line" | grep "trigger online"
if [ $? = 0 ]
then
status="online"
fi
sleep 3
logger "Status is $status"
if [[ $status="offline" ]]
then
#restart the network here
logger "do some stuff"
fi
done

if [[ $status="offline" ]]
when $status is online means
if [[ online=offline ]]
online=offline is a single string.
[[ string ]] is true if string is non zero. Which is the case.
You wanted to use the string comparison, to call [[ with 3 arguments (not counting the closing ]]), a string, a = and another string, not just one.
In other words
if [[ $status = "offline" ]]
with the correct spaces.
Another thing unrelated to your error, but related to the next one:
I surmise that all line won't contain either 'online' or 'offline'.
So, $status maybe empty (at least at the beginning. Then, it will be whatever it already was for all lines not related to your status. Which may be a 3rd error, but that an applicative problem I can not judge whether this is a problem or not. I do have the feeling that you don't want to log a status line each time a totally unrelated event is shown by journalctl tho. So you may want to reset status to "" at each loop. Not sure. Your call).
But the important point is, whatever you choose, $status maybe the empty string. In which case, your if will become if [[ =offline ]] which is true, and mine if [[ = offline ]] which is a syntax error.
To avoid that, you want to enclose $status in double quotes
So :
if [[ "$status" = "offline ]]

Related

What's wrong with my bash script? It cannot specify my OS type

#!/bin/bash
if [ ["$OSTYPE" == "linux-gnu"*] ]; then
SCRIPT_PATH=$(dirname $(realpath -s $0))
elif [ ["$OSTYPE" == "darwin"*] ]; then
SCRIPT_PATH=$(dirname $(pwd))
echo "mac!!"
else
echo "Unknown OS!"
exit
fi
I want to write a bash script to specify the OS type.
But on my MacOS, the result shows "Unknown OS!", which is wrong.
I tried echo $OSTYPE in terminal, it shows darwin20.0.
So I wonder what's the problem in my code?
The case statement is specifically intended for comparing a single string against various patterns, and doing different things depending on which it matches:
#!/bin/bash
case "$OSTYPE" in
"linux-gnu"* )
script_path="$(dirname "$(realpath -s "$0")")" ;;
"darwin"* )
script_path="$(dirname "$(pwd)")" ;;
* )
echo "Unknown OS!" >&2
exit 1 ;;
esac
Notes: each pattern is delimited with a ) at the end. You can also put a ( at the beginning, but most people don't bother. Each case ends with a double semicolon. The * case at the end will match anything that didn't match an earlier pattern, so it functions like an else clause in an if ... elif ... statement.
Some other changes I made:
It's a good idea to double-quote variable references and command substitutions (e.g. "$(realpath -s "$0")" instead of just $(realpath -s $0)) to avoid weird parsing problems with some characters (mostly spaces) in values. (There are some places where it's safe to leave the double-quotes off, but it's not worth trying to remember where they are.)
Since there are a whole bunch of all-caps names with special functions, it's safest to use lower- or mixed-case names (e.g. script_path instead of SCRIPT_PATH) to avoid conflicts.
Error and status messages (like "Unknown OS!") should generally be sent to standard error instead of standard output. I used >&2 to redirect the message to standard error.
When a script (or function, or program, or whatever) exits after an error, it should return a nonzero exit status to indicate that it failed. Different codes can be used to indicate different problems, but 1 is commonly used as a generic "something went wrong" code, so I used exit 1 here.
And I recommend using shellcheck.net to scan your scripts for common mistakes. It'll save you a lot of trouble.
Make sure you have no spaces between your opening and closing brackets, i.e., [[ and ]] vs [ [ and ] ] and you may get rid of the quotes in your patterns:
#!/usr/bin/env bash
OSTYPE=linux-gnu-123
if [[ "$OSTYPE" == linux-gnu* ]]; then
echo "linux"
elif [[ "$OSTYPE" == darwin* ]]; then
echo "mac"
else
echo "Unknown OS!"
fi
Also, use https://www.shellcheck.net/ to verify your scripts.
The problem is your attempt checking wildcard expressions via =="..."*. This needs to be done via grep. Try something like this:
#!/usr/bin/env bash
# define method
function checkOS() {
local os="$OSTYPE";
if [[ "$os" == "msys" ]]; then
echo "windows";
elif ( echo "$os" | grep -Eq "^darwin.*$" ); then
echo "mac";
elif ( echo "$os" | grep -Eq "^linux-gnu.*$" ); then
echo "linux";
else
echo "Unknown OS!" >> /dev/stderr;
exit 1;
fi
}
# try method
os="$( checkOS )";
echo -e "Current OS is \033[1m${os}\033[0m.";

Getting piped data to functions

Example output
Say I have a function, a:
function a() {
read -r VALUE
if [[ -n "$VALUE" ]]; then # empty variable check
echo "$VALUE"
else
echo "Default value"
fi
}
So, to demonstrate piping to that function:
nick#nick-lt:~$ echo "Something" | a
Something
However, piping data to this function should be optional. So, this should also be valid. and give the following output:
nick#nick-lt:~$ a
Default value
However, the function hangs, as the read command waits for data from stdin.
What I've tried
Honestly not a lot, because I don't know much about this, and searching on Google returned very little.
Conceptually, I thought there might be a way to "push" an empty (or whitespace, whatever works) value to the stdin stream, so that even empty stdin at least has this value appended/prepended, triggering read and then simply trim off that first/last character. I didn't find a way to do this.
Question
How can I, if possible, make both of the above scenarios work for function a, so that piping is optional?
EDIT: Apologies, quickly written question. Should work properly now.
One way is to check whether standard input (fd 0) is a terminal. If so, don't read, because that will cause the user to have to enter something.
function a() {
value=""
if [ \! -t 0 ] ; then # read only if fd 0 is a pipe (not a tty)
read -r value
fi
if [ "$value" ] ; then # if nonempty, print it!
echo "$value"
else
echo "Default value"
fi
}
I checked this on cygwin: a prints "Default value" and echo 42 | a prints "42".
Two issues:
Syntactic, You need a space, before closing ]]
Algorithmic, You need the -n (non-zero length) variable test, not -z (zero length)
So:
if [[ -n "$VALUE" ]]; then
Or simply:
if [[ "$VALUE" ]]; then
As [[ is a shell builtin, you don't strictly need the double quotes:
if [[ $VALUE ]]; then
Also refrain from using all uppercases as variable name, as these are usually used for denoting environment variables, and your defined one might somehow overwrite already existing one. So use lowercase variable name:
if [[ $value ]]; then
unless you are export-ing your variable, and strictly need it to be uppercased, also make sure it is not overwriting any already existing one.
Also, i would add a timeout to read e.g. -t 5 for 5 seconds, and if no input is entered, print the default value. Also change the function name to something more meaningful.
Do:
function myfunc () {
read -rt5 value
if [[ "$value" ]]; then
echo "$value"
else
echo "Default value"
fi
}
Example:
$ function myfunc () { read -rt5 value; if [[ "$value" ]]; then echo "$value"; else echo "Default value"; fi ;}
$ myfunc
Default value
$ echo "something" | myfunc
something
$ myfunc
foobar
foobar

UNIX shell scripting if and grep command setting

To design a shell that accepts an input string which is a state name, and looks for all the universities that are in that state. If found, it displays all the universities as output, otherwise it displays an error message like “xxx was not found in the file”. Here xxx is the input string. (Hint: This can be done by redirecting the search results to a file and then checking whether the file is empty or not). For example, if the input string is “NSW”, the output should be a list of all the universities in NSW. If the input is “AUS”, an error message should be displayed, saying that “AUS was not found in the file”.
Here is my code:
#!/bin/sh
echo "Please enter State of Uni (e.g NSW ; NAME MUST BE UPPER CASE)"
read State
if [ -n $State ]
then
grep "$State" Aus-Uni.txt
else
echo "$State was not found in the file"
fi
exit
There is no false statement popping up even the string that I entered was not found in the file. Somehow the true statement is roughly executed.
Firstly, you've no way to check whether the user input is compliant with your requirement that it should be all upper-case.
You could use [ shell param expansion ] to convert the input to all-uppercase before processing, well, something like :
echo "Please enter State of Uni (e.g NSW)"
read State
State="${State^^}" # Check ${parameter^^pattern} in the link
Change
if [ -n $State ]
to
if [ -n "$State" ]
# You need to double-quote the arguments for n to work
# You can't use single quotes though because variable expansion won't happen inside single quotes
This only checks whether the string is nonempty
[[ -n $State ]]
The grep runs if the check succeeds - but the success of grep is not checked
Try this
if [[ -n $State ]]; then
if ! grep "$State" Aus-Uni.txt; then
echo "$State was not found in the file"
exit 2
fi
else
echo "State is empty"
exit 1
fi

Issue with all conditions (configuration profiles) being checked

Ok, so now I have it setup like this but still it is giving me a ; exit;
logout
[Process completed]
when run through the terminal I know that there are configuration profiles that are not on my computer that should make the script continue or rather to keep looping. What is this not happening?
Thanks in advance....!
Here is what i have so far:
#!/bin/bash
profilesInstalled=`profiles -P|awk '/attribute/ {print $4}'`
while read line ; do
if [ "$line" = "F2CC78D2-A63F-45CB-AE7D-BF2221D41218" ];then
echo "AD Binding is present"
elif [ "$line" = "1C94DAD1-5FC7-46CE-9E09-576841C15093" ];then
echo "Energy Saver is present"
elif [ "$line" = "A0E5B977-F0AF-44C9-8001-DA0511B702B8" ];then
echo "Finder is present"
elif [ "$line" = "5E9DE5BF-34E4-4A7F-AA29-461FB0631943" ];then
echo "FV2 Redirect is present"
elif [ "$line" = "9AE91C88-D1B2-4227-9E95-80F492DCAA11" ];then
echo "Login Window/Security and Privacy is present"
elif [ "$line" = "00000000-0000-0000-A000-4A414D460003" ];then
echo "MDM Profile is present"
elif [ "$line" = "5E85BBF0-3483-4C80-A1FC-70AF20F82E7C" ];then
echo "Restrictions is present"
elif [ "$line" = "E433D546-5502-4C3F-9E5F-4732ED1F0032" ];then
echo "SAC SUBCA-01 is present"
elif [ "$line" = "5C2AE16B-D4E9-4D15-B190-3CD7B28779E8" ];then
echo "SAC SUBCA-02 is present"
elif [ "$line" = "2C620A13-DF1E-4F6A-A32B-9FA3149F8A56" ];then
echo "SAC-CA-01 is present"
elif [ "$line" = "3B44AE14-E0CE-4621-BACF-1A9C3BA4A459" ];then
echo "Screensaver is present"
elif [ "$line" = "396A9D84-A9CA-4575-8D09-C9F054B76AF7" ];then
echo "Spotlight is present"
elif [ "$line" = "E0138F02-9A15-47BD-8CA5-7D1D0985A1A6" ];then
echo "Workday Corp is present"
fi
exit 0
done <<<"$profilesInstalled"
You need a space around the = in those tests. That first test will always pass as written.
You should also quote the "$line" variable expansion.
Unless you use $profilesInstalled somewhere else you don't need that variable at all and can just pipe the profiles pipeline to the while loop directly.
You can also replace grep in that pipeline with awk '/attribute/ {print $4}'.
Some "meta" remarks first:
Please don't change your original question substantially, as that can invalidate existing answers (such as Etan Reisner's helpful answer)
Instead, add later changes to the original question and mark the changes as such.
If a different (follow-up) question arises, ask it as a separate, new question.
Please learn how to format your code properly - it was done for you, and you managed to destroy that formatting again with your later edits.
Please read about how to provide an MCVE (a Minimal, Complete, and Verifiable Example).
Not doing these things:
makes it far less likely that you'll get the help you need.
makes your question and its answers less valuable to future readers.
Here's a cleaned-up version of your code:
# Helper function to determine a string's element index in an array.
# SYNOPSIS
# indexOf needle "${haystack[#]}"
# *Via stdout*, returns the zero-based index of a string element in an array of strings or -1, if not found.
# The *return code* indicates if the element was found or not.
indexOf() {
local e ndx=-1
for e in "${#:2}"; do (( ++ndx )); [[ "$e" == "$1" ]] && echo $ndx && return 0; done
echo '-1'; return 1
}
# Define array of profile IDs, and parallel ID of profile names.
# Note: in bash 4+, this could be handled more elegantly with a single
# associative array.
profileIds=( F2CC78D2-A63F-45CB-AE7D-BF2221D41218 1C94DAD1-5FC7-46CE-9E09-576841C15093
A0E5B977-F0AF-44C9-8001-DA0511B702B8 5E9DE5BF-34E4-4A7F-AA29-461FB0631943
9AE91C88-D1B2-4227-9E95-80F492DCAA11 00000000-0000-0000-A000-4A414D460003
5E85BBF0-3483-4C80-A1FC-70AF20F82E7C E433D546-5502-4C3F-9E5F-4732ED1F0032
5C2AE16B-D4E9-4D15-B190-3CD7B28779E8 2C620A13-DF1E-4F6A-A32B-9FA3149F8A56
3B44AE14-E0CE-4621-BACF-1A9C3BA4A459 396A9D84-A9CA-4575-8D09-C9F054B76AF7
E0138F02-9A15-47BD-8CA5-7D1D0985A1A6 )
profileNames=( "AD Binding" "Energy Saver"
"Finder" "FV2 Redirect"
"Login Window/Security and Privacy" "MDM Profile"
"Restrictions" "SAC SUBCA-01"
"SAC SUBCA-02" "SAC-CA-01"
"Screensaver" "Spotlight"
"Workday Corp" )
# Feeding the list of installed profile IDs via a process
# substitution (<(...)), loop over them and print their
# respective names.
while read -r line ; do
# Find the line in the array of profile IDs and
# print the corresponding name.
if ndx=$(indexOf "$line" "${profileIds[#]}"); then
echo "${profileNames[ndx]} is present"
else
echo "WARNING: Unknown profile: $line" >&2
fi
done < <(profiles -P | awk '/attribute/ {print $4}')
As for why your code didn't loop:
You have an unconditional exit 0 statement in your loop, which means that the loop is always exited after the 1st line.
Due to using <<< to feed the list of profiles, you always get at least 1 line of input, because <<< appends a trailing newline to its input. If the input is empty, you'll get one iteration with an empty line.
As for this message:
; exit;
logout
[Process completed]
It tells me two things:
You're on OSX, and you ran the script from Finder.
The script produced no output (if there had been output, it would have printed between the exit; and logout lines).
When you run a script from Finder, the shell that is used to run the script exits after the script has run - and whether its Terminal window stays open or not depends on your Terminal preferences - in your case, the window stays open, but since the shell has exited, you can't interact with it anymore.
Either way, your particular script should yield the same output, whether it is run from Terminal directly, or via Finder.

Finding a part of a string in another string variable in bash

I have an issue in finding a part of string variable in another string variable, I tried many methods but none worked out..
for example:
echo -e " > Required_keyword: $required_keyword"
send_func GUI WhereAmI
echo -e " > FUNCVALUE: $FUNCVALUE"
flag=`echo $FUNCVALUE|awk '{print match($0,"$required_keyword")}'`;
if [ $flag -gt 0 ];then
echo "Success";
else
echo "fail";
fi
But it always gives fail though there are certain words in variable which matches like
0_Menu/BAA_Record ($required_keyword output string)
Trying to connect to 169.254.98.226 ... OK! Executing sendFunc GUI
WhereAmI Sent Function WhereAmI [OK PageName:
"_0_Menu__47__BAA_Record" ($FUNCVALUE output string)
As we can see here the BAA_Record is common in both of the output still, it always give FAIL
The output echo is
> Required_keyword: 0_Menu/BAA_Record
> FUNCVALUE:
Trying to connect to 169.254.98.226 ... OK!
Executing sendFunc GUI WhereAmI
Sent Function WhereAmI [OK]
PageName: "_0_Menu__47__BAA_Record"
Bash can do wildcard and regex matches inside double square brackets.
if [[ foobar == *oba* ]] # wildcard
if [[ foobar =~ fo*b.r ]] # regex
In your example:
if [[ $FUNCVALUE = *$required_keyword* ]]
if [[ $FUNCVALUE =~ .*$required_keyword.* ]]
Not sure if I understand what you want, but if you need to find out if there's part of string "a" present in variable "b" you can use simply just grep.
grep -q "a" <<< "$b"
[[ "$?" -eq 0 ]] && echo "Found" || echo "Not found"
EDIT: To clarify, grep searches for string a in variable b and returns exit status (see man grep, hence the -q switch). After that you can check for exit status and do whatever you want (either with my example or with regular if statement).

Resources