Bourne Shell Conditional Operators - conditional-statements

I am having a lot of fun playing with Bourne Shell, but I am facing a quite cryptic situation regarding conditions:
#! /bin/sh
a=1
b=2
c="0 kB/s"
if [ "$a" -eq 1 ] ; then echo "a = 1: true" ; else echo "a = 1: false" ; fi
if [ "$b" -gt 0 ] ; then echo "b > 0: true" ; else echo "b > 0: false" ; fi
if [ "$c" != "0 kB/s" ] ; then echo "c <> 0: true" ; else echo "c <> 0: false" ; fi
if [ "$a" -eq 1 ] || [ "$b" -gt 0 ] ; then echo "a = 1 or b > 0: true" ; else echo "a = 1 or b > 0: false" ; fi
if [ "$a" -eq 1 ] || [ "$b" -gt 0 ] && [ "$c" != "0 kB/s" ] ; then echo "a = 1 or b > 0 and c <> 0: true" ; else echo "a = 1 or b > 0 and c <> 0: false" ; fi
if [ true ] || [ true ] && [ false ] ; then echo "true or true and false: true" ; else echo "true or true and false: false" ; fi
gives me the following result:
a = 1: true
b > 0: true
c <> 0: false
a = 1 or b > 0: true
a = 1 or b > 0 and c <> 0: false
true or true and false: true
Short question: why don't I get a = 1 or b > 0 and c <> 0: true?
Thanks a lot for your help ...

|| and && have equal precedence, unlike in languages where the logical AND operator binds more tightly than the logical OR. This means your code as written is equivalent to
if { [ "$a" -eq 1 ] || [ "$b" -gt 0 ]; } && [ "$c" != "0 kB/s" ] ; then
echo "a = 1 or b > 0 and c <> 0: true"
else
echo "a = 1 or b > 0 and c <> 0: false"
fi
rather than the expected
if [ "$a" -eq 1 ] || { [ "$b" -gt 0 ] && [ "$c" != "0 kB/s" ]; } ; then
echo "a = 1 or b > 0 and c <> 0: true"
else
echo "a = 1 or b > 0 and c <> 0: false"
fi

Related

Set environment variable with cron. Not working

How can I set for example with cronjob
1/* * * * * bash -c 'export TZ=Europe/London'
1/* * * * * bash -c '. /path/to/script.sh'
This script has multiple exports which is getting the timezone offset and would like that cronjob update the system var TZ with that one.
If I run it from shell it works with
". /path/to/script.th" as source but not in a cronjob.
At least how to set the environment variables with cronjob.
I also have tried that the script writes into the /etc/environment
TZ=Europe/London
and cronjob to set -a;. /etc/environment; set +a;
None are working but they do from shell.
Here is my script. What are my options from here when the script is ran by cronjob or anything else...Also tried to make it run with supervisord...same result.
Everything is working running locally from shell, but can't figure out how to modify the system "$TZ" environment with a script or from cronjob.
The script writes into the /etc/environment but when I set it from cron still won't update but it will from shell.
I know it all works from current shell but /etc/environment should be available to all.
I have edited so the way to go is write with the script into the /etc/profile.d/custom.sh
This works also inside a docker container to update periodicaly environmental variable if needed.
#!/bin/bash
#######################
## Preload variables ##
#######################
. /etc/profile
. ~/.bash_profile
. ~/.bashrc
#############################
## Get Boardtime from API ##
#############################
boardtime=$(curl -s http://server/api/get-time \
-u user:pass \
-d locale='en_US' | \
grep -oP '(?<=boardTime":")[^","boardTimeLocalized"]*' | \
sed -r -e 's/^.{8}/& /' -e 's/[^ ]{2}/&:/5g' | \
sed 's/:$//')
formatted_boardtime=$(date -d "$boardtime" "+%a, %e %b %Y %H:%M:%S %z")
echo -ne "\nLocaltime is $formatted_boardtime!\n"
###################################
## Get timezone offset from ship ##
###################################
minutes=$(curl -s http://server/timezone/currentOffset.txt | grep -oP "[-+][0-9]{0,9}/|[0-9]{0,9}")
echo -ne "\nTimezone offset is $minutes\n"
((h=$minutes / 60))
#############################
## Set Offset Symbol Value ##
#############################
symbol=$(echo $minutes | grep -oP "[-]")
digit=$(echo $h | grep -oP "[0-9]{2,4}")
if [[ $symbol == "-" ]] && [[ $digit ]]; then
offset="-"$h"00"
elif [[ $symbol != "-" ]] && [[ $digit ]]; then
offset="+"$h"00"
elif [[ $symbol == "-" ]] && [[ ! $digit ]]; then
offset="-0$(echo "$h" | grep -oP '[0-9]{0,4}')00"
else
offset="+0$(echo "$h" | grep -oP '[0-9]{0,4}')00"
fi
##########################
## Set Current timezone ##
##########################
localtime=$(echo $formatted_boardtime | sed -E "s/([-+][0-9]{4})$/"$offset"/")
if [[ $symbol == "-" && $offset == -1100 ]]; then
export TZ="US/Samoa"
elif [[ $symbol == "-" && $offset == -1000 ]]; then
export TZ="Pacific/Honolulu"
elif [[ $symbol == "-" && $offset == -0900 ]]; then
export TZ="America/Nome"
elif [[ $symbol == "-" && $offset == -0800 ]]; then
export TZ="America/Los_Angeles"
elif [[ $symbol == "-" && $offset == -0700 ]]; then
export TZ="America/Denver"
elif [[ $symbol == "-" && $offset == -0600 ]]; then
export TZ="America/Cancun"
elif [[ $symbol == "-" && $offset == -0500 ]]; then
export TZ="America/Lima"
elif [[ $symbol == "-" && $offset == -0400 ]]; then
export TZ="America/Santiago"
elif [[ $symbol == "-" && $offset == -0300 ]]; then
export TZ="America/Bahia"
elif [[ $symbol == "-" && $offset == -0200 ]]; then
export TZ="America/Noronha"
elif [[ $symbol == "-" && $offset == -0100 ]]; then
export TZ="Atlantic/Azores"
elif [[ $symbol != "-" && $offset == +0000 ]]; then
export TZ="Europe/London"
elif [[ $symbol != "-" && $offset == +0100 ]]; then
export TZ="Europe/Berlin"
elif [[ $symbol != "-" && $offset == +0200 ]]; then
export TZ="Europe/Athens"
elif [[ $symbol != "-" && $offset == +0300 ]]; then
export TZ="Asia/Qatar"
elif [[ $symbol != "-" && $offset == +0400 ]]; then
export TZ="Asia/Dubai"
elif [[ $symbol != "-" && $offset == +0500 ]]; then
export TZ="Indian/Maldives"
elif [[ $symbol != "-" && $offset == +0600 ]]; then
export TZ="Asia/Thimbu"
elif [[ $symbol != "-" && $offset == +0700 ]]; then
export TZ="Asia/Bangkok"
elif [[ $symbol != "-" && $offset == +0800 ]]; then
export TZ="Asia/Hong_Kong"
elif [[ $symbol != "-" && $offset == +0900 ]]; then
export TZ="Asia/Tokyo"
elif [[ $symbol != "-" && $offset == +1000 ]]; then
export TZ="Australia/Melbourne"
elif [[ $symbol != "-" && $offset == +1100 ]]; then
export TZ="Pacific/Ponape"
elif [[ $symbol != "-" && $offset == +1200 ]]; then
export TZ="Pacific/Fiji"
elif [[ $symbol != "-" && $offset == +1300 ]]; then
export TZ="Pacific/Apia"
elif [[ $symbol != "-" && $offset == +1400 ]]; then
export TZ="Pacific/Kiritimati"
fi
echo -ne "\nLocaltime is $localtime!\n"
echo -ne "\nTimezone matching offset is set $TZ!\n"
echo "$TZ" > /etc/timezone
echo "TZ=$TZ" > /etc/profile.d/custom.sh
###########################################################
## Restart postfix to apply new timezone also to postfix ##
###########################################################
/usr/sbin/postfix stop && /usr/sbin/postfix start
Can mark as a working solution for this using as follows then:
The solution is create a /etc/profile.d/custom.sh
Write in there you environment variable or your script can write in there and create the file with the content you need.
Inside there simply add all the environments you need like
export TZ=Europe/London
export MYENV=value
export etc..
..

AWK new line sorting

I have a script that sorts numbers:
{
if ($1 <= 9) xd++
else if ($1 > 9 && $1 <= 19) xd1++
else if ($1 > 19 && $1 <= 29) xd2++
else if ($1 > 29 && $1 <= 39) xd3++
else if ($1 > 39 && $1 <= 49) xd4++
else if ($1 > 49 && $1 <= 59) xd5++
else if ($1 > 59 && $1 <= 69) xd6++
else if ($1 > 69 && $1 <= 79) xd7++
else if ($1 > 79 && $1 <= 89) xd8++
else if ($1 > 89 && $1 <= 99) xd9++
else if ($1 == 100) xd10++
} END {
print "0-9 : "xd, "10-19 : " xd1, "20-29 : " xd2, "30-39 : " xd3, "40-49 : " xd4, "50-59 : " xd5, "60-69 : " xd6, "70-79 : " xd7, "80-89 : " xd8, "90-99 : " xd9, "100 : " xd10
}
output:
$ cat xd1 | awk -f script.awk
0-9 : 16 10-19 : 4 20-29 : 30-39 : 2 40-49 : 1 50-59 : 1 60-69 : 1 70-79 : 1 80-89 : 1 90-99 : 1 100 : 2
how to make that every tenth was on a new line?
like this:
0-9 : 16
10-19 : 4
20-29 :
30-39 : 2
print with \n doesn't work
additionally:
in the top ten I have 16 numbers, how can I get this information using the "+" sign
like this:
0-9 : 16 ++++++++++++++++
10-19 : 4 ++++
20-29 :
30-39 : 2 ++
thank you in advance
If we rewrite the current code to use an array to keep track of counts, we can then use a simple for loop to print the results on individual lines, eg:
{ if ($1 <= 9) xd[0]++
else if ($1 <= 19) xd[1]++
else if ($1 <= 29) xd[2]++
else if ($1 <= 39) xd[3]++
else if ($1 <= 49) xd[4]++
else if ($1 <= 59) xd[5]++
else if ($1 <= 69) xd[6]++
else if ($1 <= 79) xd[7]++
else if ($1 <= 89) xd[8]++
else if ($1 <= 99) xd[9]++
else xd[10]++
}
END { for (i=0;i<=9;i++)
print (i*10) "-" (i*10)+9, ":", xd[i]
print "100 :", xd[10]
}
At this point we could also replace the 1st part of the script with a comparable for loop, eg:
{ for (i=0;i<=9;i++)
if ($1 <= (i*10)+9) {
xd[i]++
next
}
xd[10]++
}
END { for (i=0;i<=9;i++)
print (i*10) "-" (i*10)+9, ":", xd[i]
print "100 :", xd[10]
}
As for the additional requirement to print a variable number of + on the end of each line we can add a function (prt()) to generate the variable number of +:
function prt(n ,x) {
x=""
if (n) {
x=sprintf("%*s",n," ")
gsub(/ /,"+",x)
}
return x
}
{ for (i=0;i<=9;i++)
if ($1 <= (i*10)+9) {
xd[i]++
next
}
xd[10]++
}
END { for (i=0;i<=9;i++)
print (i*10) "-" (i*10)+9, ":", xd[i], prt(xd[i])
print "100 :", xd[10], prt(xd[10])
}
how to make that every tenth was on a new line?
Inform GNU AWK that you want OFS (output field separator) to be newline, consider following simple example
awk 'BEGIN{x=1;y=2;z=3}END{print "x is " x, "y is " y, "z is " z}' emptyfile
gives output
x is 1 y is 2 z is 3
whilst
awk 'BEGIN{OFS="\n";x=1;y=2;z=3}END{print "x is " x, "y is " y, "z is " z}' emptyfile
gives output
x is 1
y is 2
z is 3
Explanation: OFS value (default: space) is used for joining arguments of print. If you want to know more about OFS then read 8 Powerful Awk Built-in Variables – FS, OFS, RS, ORS, NR, NF, FILENAME, FNR
(tested in gawk 4.2.1)
you don't need to hard-code in 10-buckets like that :
jot -r 300 1 169 | mawk '
BEGIN { _+=(_+=_^=_<_)*_*_ } { ++___[_<(__=int(($!!_)/_))?_:__] }
END {
____ = sprintf("%*s", NR, _)
gsub(".","+",____)
for(__=_-_;__<=_;__++) {
printf(" [%3.f %-6s] : %5.f %.*s\n",__*_,+__==+_?"+ "\
: " , " __*_--+_++, ___[__], ___[__], ____) } }'
[ 0 , 9 ] : 16 ++++++++++++++++
[ 10 , 19 ] : 17 +++++++++++++++++
[ 20 , 29 ] : 16 ++++++++++++++++
[ 30 , 39 ] : 19 +++++++++++++++++++
[ 40 , 49 ] : 14 ++++++++++++++
[ 50 , 59 ] : 18 ++++++++++++++++++
[ 60 , 69 ] : 18 ++++++++++++++++++
[ 70 , 79 ] : 16 ++++++++++++++++
[ 80 , 89 ] : 20 ++++++++++++++++++++
[ 90 , 99 ] : 19 +++++++++++++++++++
[100 + ] : 127 ++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++

result not showing when looping

I'm doing a powershell which runs through a loop to get result from an sql query with different parameters. the issue is only the first run of loop has a result. the rest all blank result which should also show some result.
$Days = #(90,60,30,15)
$Start = 0
$End = 0
$Affiliation = 1
for ($i = 0 ; $i -lt $Affiliation.Count ; $i++) {
for ($i = 0; $i -lt $Days.Count ; $i++){
if ($Days[$i] -eq 1) {
$End = $Days[$i]
$Start = 0
write-host "Start/End r:" $Start $End
}
elseIf ($index -eq $Days.Length) {
$End = $Days[$i]
$Start = 0
write-host "Start/End m:" $Start $End
}
else {
$End = $Days[$i]
$Start = $Days[$i+1] + 1
write-host "Start/End s:" $Start $End
}
########## Queries
$Querydays = " SELECT
distinct tblcustomer.CustomerName AS Customer_Name
, tblsma.sorno AS SMA_SOR_Number
, FUNC_GET_JSON_VALUES('Name', tblsma.products) AS Products
, DATE_FORMAT(tblsma.expiryDate, '%m/%d/%Y') AS Expiry_Date
, tblsma.Remarks AS 'Remarks'
, DATEDIFF( DATE(tblsma.expiryDate), CURDATE()) AS Days_Left_Before_Expiration
#,GROUP_CONCAT(tblsma.products) AS 'PRODUCT'
FROM
where
tblcustomer.affiliationID = '" + $Affiliation[$i] + "'
and tblcustomer.affiliationID <> 95
and tblaccountsgroup.GroupID <> 5
and datediff(DATE(tblsma.expiryDate), CURDATE()) between " + $Start + " and " + $End + "
order by Days_Left_Before_Expiration DESC"
#####################
$ResultDays=$null
$ResultDays = MysqlConn -Query $Querydays
Write-Host $ResultDays
$EmailBody = $EmailBody + "`r `n" + "<b>List of Valid License that will due in next " + $Days[$i] + " days:</b>"
foreach($row in $ResultDays)
{
$EmailBody = "`n" + $EmailBody + "`r `n" + "Account Name : " + $row.Item(0) + "`r" +
"SOR No. : " + $row.Item(1) + "`r" +
"Product Name : " + $row.Item(2) + "`r" +
"Expiry Date : " + $row.Item(3) + "`r" +
"Remarks : " + $row.Item(4) + "`r" +
"Days Before Expiry : " + $row.Item(5) + "`r" +
"`n"
}
$rep = $Days[$i+1]
}
}
Start/End s: 61 90
System.Data.DataRow System.Data.DataRow System.Data.DataRow
Start/End s: 31 60
Start/End s: 16 30
Start/End s: 1 15
Because you use in both forloops the same variable $i this will be overwritten all the time. Change the name of one of them.
Bad:
for ($i = 1; $i -lt 11; $i ++)
{
('First for loop: ' + $i)
for ($i = 1; $i -lt 11; $i ++)
{
('Second for loop: ' + $i)
}
}
First for loop: 1
Second for loop: 1
Second for loop: 2
Second for loop: 3
Second for loop: 4
Second for loop: 5
Second for loop: 6
Second for loop: 7
Second for loop: 8
Second for loop: 9
Second for loop: 10
Good:
for ($i = 1; $i -lt 11; $i ++)
{
('First for loop: ' + $i)
for ($x = 1; $x -lt 11; $x ++)
{
('Second for loop: ' + $x)
}
}
First for loop: 1
Second for loop: 1
Second for loop: 2
Second for loop: 3
Second for loop: 4
Second for loop: 5
Second for loop: 6
Second for loop: 7
Second for loop: 8
Second for loop: 9
Second for loop: 10
First for loop: 2
Second for loop: 1
Second for loop: 2
...

Why do Perl 6 state variable behave differently for different files?

I have 2 test files. In one file, I want to extract the middle section using a state variable as a switch, and in the other file, I want to use a state variable to hold the sum of numbers seen.
File one:
section 0; state 0; not needed
= start section 1 =
state 1; needed
= end section 1 =
section 2; state 2; not needed
File two:
1
2
3
4
5
Code to process file one:
cat file1 | perl6 -ne 'state $x = 0; say " x is ", $x; if $_ ~~ m/ start / { $x = 1; }; .say if $x == 1; if $_ ~~ m/ end / { $x = 2; }'
and the result is with errors:
x is (Any)
Use of uninitialized value of type Any in numeric context
in block at -e line 1
x is (Any)
= start section 1 =
x is 1
state 1; needed
x is 1
= end section 1 =
x is 2
x is 2
And the code to process file two is
cat file2 | perl6 -ne 'state $x=0; if $_ ~~ m/ \d+ / { $x += $/.Str; } ; say $x; '
and the results are as expected:
1
3
6
10
15
What make the state variable fail to initialize in the first code, but okay in the second code?
I found that in the first code, if I make the state variable do something, such as addition, then it works. Why so?
cat file1 | perl6 -ne 'state $x += 0; say " x is ", $x; if $_ ~~ m/ start / { $x = 1; }; .say if $x == 1; if $_ ~~ m/ end / { $x = 2; }'
# here, $x += 0 instead of $x = 0; and the results have no errors:
x is 0
x is 0
= start section 1 =
x is 1
state 1; needed
x is 1
= end section 1 =
x is 2
x is 2
Thanks for any help.
This was answered in smls's comment:
Looks like a Rakudo bug. Simpler test-case:
echo Hello | perl6 -ne 'state $x = 42; dd $x'.
It seems that top-level state variables are
not initialized when the -n or -p switch is used. As a work-around, you can manually initialize the variable in a separate statement, using the //= (assign if undefined) operator:
state $x; $x //= 42;

How to compare number of lines of two files using Awk

I am new to awk and need to compare the number of lines of two files.
The script shall return true,
if lines(f1) == (lines(f2)+1)
otherwise false. How can I do that?
Best regards
If it has to be awk:
awk 'NR==FNR{x++} END{ if(x!=FNR){exit 1} }' file1 file2
The varibale x is incremented and contains the number of line of file1 and FNR contains the number of file2. At the end, both are compared and the script is exited 0 or 1.
See an example:
user#host:~$ awk 'NR==FNR{x++} END{ if(x!=FNR){exit 1} }' shortfile longfile
user#host:~$ echo $?
1
user#host:~$ awk 'NR==FNR{x++} END{ if(x!=FNR){exit 1} }' samefile samefile
user#host:~$ echo $?
0
Something like this should suit your purposes:
[ oele3110 $] cat line_compare.awk
#!/usr/bin/gawk -f
NR==FNR{
n_file1++;
}
NR!=FNR{
n_file2++;
}
END{
n_file2++;
if(n_file1==n_file2){exit(1);}
}
[ oele3110 $] cat f1
1
1
1
1
1
1
[ oele3110 $] cat f2
1
1
1
1
1
[ oele3110 $] cat f3
1
1
1
1
1
[ oele3110 $]
[ oele3110 $] wc -l f*
6 f1
5 f2
5 f3
16 total
[ oele3110 $] ./line_compare.awk f1 f2
[ oele3110 $] echo $?
1
[ oele3110 $] ./line_compare.awk f2 f3
[ oele3110 $] echo $?
0
[ oele3110 $]
Actually, I think I should have asked you to invest a bit more effort before giving you the answer. I'll leave it for now, but next time I won't make the same mistake.