Two-words argument in shell script - unix - bash

I have a problem with a shell script. It is supposed to read arguments: index, date, time1 (the beginning of the interval), time2 (the end of the interval).
It should count how many times the user(index) has logged on the given date in the time interval time1-time2.
For example : 121212 "Jan 14" 00 12
This works, but I have a problem with the argument date. It doesn't recognize it as one argument. It splits it in Jan and 14" which is a big problem.
I've been searching on the internet for a few hours, but I couldn't find solution anywhere.
Here is my code:
#!/bin/bash
read user date time1 time2
list=`last | grep ^$user.*$date | awk '{ print $7 }'`
start=$time1
end=$time2
echo "start $start"
echo "end $end"
count=0
for el in $list ;
do
login=$el
echo "najava $najava"
checkIf(){
current=$login
[[ ($start = $current || $start < $current) && ($current = $end || $current < $end) ]]
}
if checkIf; then
count=`expr $count + 1`
ip=`last | grep ^$user.*$date.*$login | awk '{ print $3 }'`
echo $ip >> address.txt
else
continue
fi
done
echo "The user has logged in $count times in the given time interval"

From the man entry for read,
The characters in IFS are used to split the line into words.
And the default IFS on your system is likely the space. You can immediately fix your issue by escaping any spaces that you do not want to use as word separators:
121212 Jan\ 14 00 12
should work for your purposes.
But, of course, this is not an ideal solution. One possibility is to just pass your arguments to the script on the command line rather than through read after the script has been invoked, e.g.
#!/bin/bash
user="$1"
date="$2"
time1="$3"
time2="$4"
#etc
See http://www.shelldorado.com/goodcoding/cmdargs.html for more detail on handling command line arguments.

Related

subtrack from list file [duplicate]

This question already has answers here:
How to read variables from file, with multiple variables per line?
(2 answers)
Closed last month.
I have stuck in subtrack from file using shell script, below my data. this file name is age
1673495825 1673420702 01/11/23_14:05:02
1673495825 1673420702 01/11/23_14:05:02
My expectation is collum 1 will be subtrack with collum 2
here my code
for i in "$(cat age)"
do
timestamp="$(echo "$i" | sed 's/ .*//' | awk '$1=$1')"
firstoccure="$(echo "$i" | awk '{print $2}' | awk '$1=$1')"
aging="$((timestamp - firstoccure))"
if [ $aging -gt 43200 -o $aging -lt 86400 ]
then
echo "12 Hours"
elif [ $aging -gt 86400 ]
then
echo "24 Hours"
fi
done
whit above script I have an error like this
***1673495776: syntax error in expression (error token is "1673495776")***
if my list only 1, the script will be success.
need your help
thanks
Expect I can solve th problem
for i in "$(cat age)" iterates only once with i equal to the whole content of the age file. Probably not what you want.
The following does everything with awk:
awk '{
a=$1-$2;
if(a>43200 && a<86400)
print "12 Hours";
else if(a>86400)
print "24 Hours"
}' age
If you really prefer using bash, you can try:
while read -r timestamp firstoccure dummy; do
((aging=timestamp-firstoccure))
if ((aging>43200)) && ((aging<86400)); then
echo "12 Hours"
elif ((aging>86400)); then
echo "24 Hours"
fi
done < age
Note: with your inequalities if aging=86400 nothing will be printed.

Simple bash program which compares values

I have a file which contains varoius data (date,time,speed, distance from the front, distance from the back), the file looks like this, just with more rows:
2003.09.23.,05:05:21:64,134,177,101
2009.03.10.,17:46:17:81,57,102,57
2018.01.05.,00:30:37:04,354,145,156
2011.07.11.,23:21:53:43,310,125,47
2011.06.26.,07:42:10:30,383,180,171
I'm trying to write a simple Bash program, which tells the dates and times when the 'distance from the front' is less than the provided parameter ($1)
So far I wrote:
#!/bin/bash
if [ $# -eq 0 -o $# -gt 1 ]
then
echo "wrong number of parameters"
fi
i=0
fdistance=()
input='auto.txt'
while IFS= read -r line
do
year=${line::4}
month=${line:5:2}
day=${line:8:2}
hour=${line:12:2}
min=${line:15:2}
sec=${line:18:2}
hthsec=${line:21:2}
fdistance=$(cut -d, -f 4)
if [ "$fdistance[$i]" -lt "$1" ]
then
echo "$year[$i]:$month[$i]:$day[$i],$hour[$i]:$min[$i]:$sec[$i]:$hthsec[$i]"
fi
i=`expr $i + 1`
done < "$input"
but this gives the error "whole expression required" and doesn't work at all.
If you have the option of using awk, the entire process can be reduced to:
awk -F, -v dist=150 '$4<dist {split($1,d,"."); print d[1]":"d[2]":"d[3]","$2}' file
Where in the example above, any record with distance (field 4, $4) less than the dist variable value takes the date field (field 1, $1) and splits() the field into the array d on "." where the first 3 elements will be year, mo, day and then simply prints the output of those three elements separated by ":" (which eliminates the stray "." at the end of the field). The time (field 2, $2) is output unchanged.
Example Use/Output
With your sample data in file, you can do:
$ awk -F, -v dist=150 '$4<dist {split($1,d,"."); print d[1]":"d[2]":"d[3]","$2}' file
2009:03:10,17:46:17:81
2018:01:05,00:30:37:04
2011:07:11,23:21:53:43
Which provides the records in the requested format where the distance is less than 150. If you call awk from within your script you can pass the 150 in from the 1st argument to your script.
You can also accomplish this task by substituting a ':' for each '.' in the first field with gsub() and outputting a substring of the first field with substr() that drops the last character, e.g.
awk -F, -v dist=150 '$4<dist {gsub(/[.]/,":",$1); print substr($1,0,length($1)-1),$2}' file
(same output)
While parsing the data is a great exercise for leaning string handling in shell or bash, in practice awk will be Orders of Magnitude faster than a shell script. Processing a million line file -- the difference in runtime can be seconds with awk compared to minutes (or hours) with a shell script.
If this is an exercise to learn string handling in your shell, just put this in your hip pocket for later understanding that awk is the real Swiss Army-Knife for text processing. (well worth the effort to learn)
Would you try the following:
#/bin/bash
if (( $# != 1 )); then
echo "usage: $0 max_distance_from_the_front" >& 2 # output error message to the stderr
exit 1
fi
input="auto.txt"
while IFS=, read -r mydate mytime speed fdist bdist; do # split csv and assign variables
mydate=${mydate%.}; mydate=${mydate//./:} # reformat the date string
if (( fdist < $1 )); then # if the front disatce is less than $1
echo "$mydate,$mytime" # then print the date and time
fi
done < "$input"
Sample output with the same parameter as Keldorn:
$ ./test.sh 130
2009:03:10,17:46:17:81
2011:07:11,23:21:53:43
There are a few odd things in your script:
Why is fdistance an array. It is not necessary (and here done wrong) since the file is read line by line.
What is the cut of the line fdistance=$(cut -d, -f 4) supposed to cut, what's the input?
(Note: When invalid parameters, better end the script right away. Added in the example below.)
Here is a working version (apart from the parsing of the date, but that is not what your question was about so I skipped it):
#!/usr/bin/env bash
if [ $# -eq 0 -o $# -gt 1 ]
then
echo "wrong number of parameters"
exit 1
fi
input='auto.txt'
while IFS= read -r line
do
fdistance=$(echo "$line" | awk '{split($0,a,","); print a[4]}')
if [ "$fdistance" -lt "$1" ]
then
echo $line
fi
done < "$input"
Sample output:
$ ./test.sh 130
2009.03.10.,17:46:17:81,57,102,57
2011.07.11.,23:21:53:43,310,125,47
$

Using AWK for a variable inside a loop

So I get some URL's from my other team and I need to identiy a defined pattern in that URL and save the value after the pattern inside a variable.
Can this be achieved ?
**Input file: Just an example**
https://stackoverflow.com/questions/hakuna
https://stackoverflow.com/questions/simba
I wrote a simple for loop for this purpose
for i in `cat inputFile`
do
storeVal=awk -v $i -F"questions/" '{print$2}'
echo "The Name for the day is ${storeVal}"
length=`secondScript.sh ${storeVal}`
if [[ $length -gt 10 ]]
then
thirdScript.sh ${storeVal}
elif [[ $length -lt 10 ]]
then
fourthScript.sh ${storeVal}
else
echo "The length of for ${storeVal} is undefined"
done
Desired Output:
The Name for the day is hakuna
The length of for hakuna is greater than 10
Command1 hakuna executed
Command2 hakuna executed
The Name for the day is simba
Command1 simba executed
Command2 simba executed
And extra point to be noted.
The reason why I need to store the awk cut value in a variable is because I need to use that variable in multiple places with the loop.
Since it sounds like you want to run a command for every line in the input file, you can just use the built-in functionality of the shell:
while IFS=/ read -ra pieces; do
printf '%s\n' "${pieces[#]}" # prints each piece on a separate line
done < inputFile
If you always want the last part of the url (i.e. after the last /) on each line, then you can use "${pieces[-1]}":
while IFS=/ read -ra pieces; do
variable=${pieces[-1]} # do whatever you want with this variable
printf 'The Name for the day is %s\n' "$variable" # e.g. print it
done < inputFile
Since you want to extract all the string after the questions/ bit, you can simply use shell pattern substitution for that.
while IFS= read -r line; do
storeVal=${line##*questions/}
echo "The Name for the day is ${storeVal}"
done < 'inputFile'
Sample input file:
$ cat inputFile
https://stackoverflow.com/questions/hakuna
https://stackoverflow.com/questions/simba
https://stackoverflow.com/questions/simba/lion
Output of script:
The Name for the day is hakuna
The Name for the day is simba
The Name for the day is simba/lion

String together awk commands

I'm writing a script that searches a file, gets info that it then stores into variables, and executes a program that I made using those variables as data. I actually have all of that working, but I need to take it a step further:
What I currently have is
#!/bin/sh
START=0
END=9
LOOP=10
PASS=0
for i in $(seq 0 $LOOP)
do
LEN=$(awk '/Len =/ { print $3; exit;}' ../../Tests/shabittestvectors/SHA1ShortMsg.rsp)
MSG=$(awk '/Msg =/ { print $3; exit; }' ../../Tests/shabittestvectors/SHA1ShortMsg.rsp)
MD=$(awk '/MD =/ { print $3; exit; }' ../../Tests/shabittestvectors/SHA1ShortMsg.rsp)
echo $LEN
echo $MSG
MD=${MD:0:-1}
CIPHER=$(./cyassl hash -sha -i $MSG -l $LEN)
echo $MD
echo $CIPHER
if [ $MD == $CIPHER ]; then
echo "PASSED"
PASS=$[PASS + 1]
echo $PASS
fi
done
if [ $PASS == $[LOOP+1] ]; then
echo "All Tests Successful"
fi
And the input file looks like this:
Len = 0
Msg = 00
MD = da39a3ee5e6b4b0d3255bfef95601890afd80709
Len = 1
Msg = 00
MD = bb6b3e18f0115b57925241676f5b1ae88747b08a
Len = 2
Msg = 40
MD = ec6b39952e1a3ec3ab3507185cf756181c84bbe2
All the program does right now, is read the first instances of the variables and loop around there. I'm hoping to use START and END to determine the lines in which it checks the file, and then increment them every time it loops to obtain the other instances of the variable names, but all of my attempts have been unsuccessful so far. Any ideas?
EDIT: Output should look something like, providing my program "./cyassl" works as it should
0
00
da39a3ee5e6b4b0d3255bfef95601890afd80709
da39a3ee5e6b4b0d3255bfef95601890afd80709
PASSED
1
00
bb6b3e18f0115b57925241676f5b1ae88747b08a
bb6b3e18f0115b57925241676f5b1ae88747b08a
PASSED
2
40
ec6b39952e1a3ec3ab3507185cf756181c84bbe2
ec6b39952e1a3ec3ab3507185cf756181c84bbe2
PASSED
etc.
There's no need to make multiple passes on the input file.
#!/bin/sh
exec < ../../Tests/shabittestvectors/SHA1ShortMsg.rsp
status=pass
awk '{print $3,$6,$9}' RS= | {
while read len msg md; do
if test "$(./cyassl hash -sha -i $msg -l $len)" = "$md"; then
echo passed
else
status=fail
fi
done
test "$status" = pass && echo all tests passed
}
The awk will read from stdin (which the exec redirects from the file; personally I would skip that line and have the caller direct input appropriately) and splits the input into records of one paragraph each. A "paragraph" here means that the records are separated by blank lines (the lines must be truly blank, and cannot contain whitespace). Awk then parses each record and prints the 3rd, 6th, and 9th field on a single line. This is a bit fragile, but for the shown input those fields represent length, message, and md hash, respectively. All the awk is doing is rearranging the input so that it is one record per line. Once the data is in a more readable format, a subshell reads the data one line at a time, parsing it into the variables named "len", "msg", and "md". The do loop processes once per line of input, spewing the rather verbose message "passed" with each test it runs (I would remove that, but retained it here for consistency with the original script), and setting the status if any tests fail. The braces are necessary to ensure that the value of the variable status is retained after the do loop terminates.
The following code,
inputfile="../../Tests/shabittestvectors/SHA1ShortMsg.rsp"
while read -r len msg md
do
echo got: LEN:$len MSG:$msg MD:$md
#cypher=$(./cyassl hash -sha -i $msg -l $len)
#continue as you wish
done < <(perl -00 -F'[\s=]+|\n' -lE 'say qq{$F[1] $F[3] $F[5]}' < "$inputfile")
for your input data, produces:
got: LEN:0 MSG:00 MD:da39a3ee5e6b4b0d3255bfef95601890afd80709
got: LEN:1 MSG:00 MD:bb6b3e18f0115b57925241676f5b1ae88747b08a
got: LEN:2 MSG:40 MD:ec6b39952e1a3ec3ab3507185cf756181c84bbe2
If your input data is in order you can have this with a simplified bash:
#!/bin/bash
LOOP=10
PASS=0
FILE='../../Tests/shabittestvectors/SHA1ShortMsg.rsp'
for (( I = 1; I <= LOOP; ++I )); do
read -r LEN && read -r MSG && read -r MD || break
echo "$LEN"
echo "$MSG"
MD=${MD:0:-1}
CIPHER=$(exec ./cyassl hash -sha -i "$MSG" -l "$LEN")
echo "$MD"
echo "$CIPHER"
if [[ $MD == "$CIPHER" ]]; then
echo "PASSED"
(( ++PASS ))
fi
done < <(exec awk '/Len =/,/Msg =/,/MD =/ { print $3 }' "$FILE")
[[ PASS -eq LOOP ]] && echo "All Tests Successful."
Just make sure you don't run it as sh e.g. sh script.sh. bash script.sh most likely.

Bash: Check current day time against "allow string"

This might be a bit strange. I have a string that indicates all of the "allowed" times per week. The String is 168 characters long. Each of the characters is either a 1 or a 0. Each character represents one hour of the week. So, the first 24 characters in the string indicate the "allowed" hours for Sunday. The next 24 characters are Monday and so on. The number 1 represents "allowed" and the number 0 represents not allowed. Here is a sample string:
000000111111111111111000000000111111111111111000000000111111111111111000000000111111111111111000000000111111111111111000000000111111111111111000000000111111111111111000
This string indicates allowed times between 6AM and 9PM Sunday through Saturday. Many times there will be different allowed hours for different days of the week. I'm trying to figure out it bash if the current day time is "allowed".
I've figured out that these tell me the current day and time:
DATE=$(date)
day=$(echo $DATE | cut -d' ' -f1)
time=$(echo $DATE | cut -d' ' -f4 | cut -d: -f1-2 | tr -d ':')
However, I can't figure out an elegant way of comparing those to my long string.
Any help would be greatly appreciated!
Please note: I am using BusyBox 1.19.4. That means that awk, sed, grep, etc may not be full-featured. :-(
EV
Here is another bash version which may help you. I am assuming that the input string is stored in a file and will be passed as an argument to the script :
i=`expr $(date +%w) \\* 24 + $(date +%H) + 1`
i=$(cat $1 | awk -v I=${i} '{print substr($0,I,1)}')
if [ $i -eq 1 ]
then echo "BUSY"
elif [ $i -eq 0 ]
then echo "FREE"
else
echo "error : input string is shorter than expected"
fi
If you want to pass the string as a parameter instead of a file, just replace the 'cat $1' part of the 2nd statement to :
echo $1
NB : In case input string is shorter than the i value, you will two error messages from shell "[: -eq: unary operator expected" for the two if conditions along with the 3rd echo statement. So you may want to improve the if conditions a bit.
Again, the above piece of code is valid only when the start of the week is Sunday 0th hour. Otherwise some other mapping must be used.
Interesting question!
Here is awk solution to do it in one shot. (without date stuff):
awk -v FIELDWIDTHS="24 24 24 24 24 24 24" '{split(strftime("%w,%H", systime()),t,",");for(i=0;i<7;i++)a[i]=$(i+1);print substr(a[t[1]],t[2]+1,1)?"OK":"NOK"}' file
for better reading:
awk -v FIELDWIDTHS="24 24 24 24 24 24 24" '{
split(strftime("%w,%H", systime()),t,",")
for(i=0;i<7;i++) a[i]=$(i+1)
print substr(a[t[1]],t[2]+1,1)?"OK":"NOK"
}' file
if I run this now, (here is Wed. 10:58 AM), it outputs:
OK
please test it with your real data, and report if it works. :)
Here is a bash only version
startoweek=`date +%s -d 20130717`
#1373999400
current=$(date +%s)
let diff=(${current}-${startoweek})/3600
if [ $diff -ge 168 ]
then echo "Old Timetable"
exit
fi
work=000000111111111111111000000000111111111111111000000000111111111111111000000000111111111111111000000000111111111111111000000000111111111111111000000000111111111111111000
allowed=${work:$diff:1}
if [ $allowed -eq 1 ]
then echo "Welcome"
elif [ $allowed -eq 0 ]
then echo "Go Home"
else
echo "Error"
fi

Resources