compare the expiration date in /etc/shadow to today's date and figure out which accounts have expired - linux

root#node033:~# vi exppass
root#node033:~# bash exppass
exppass: line 7: syntax error: unexpected end of file
root#node033:~# cat exppass
cat /etc/shadow |
while IFS=":" read col1 col2 col3 col4 col5 col6 col8;
echo $col3 $col8
if [ $expire -lt $today ];
I am trying to remove the expiration passwd using to compare the date and time.
adjust your script to do something along the lines:
if [ $expire -lt $today ]; then
#delete the password
I think $expire above is equivalent to one of the columns you're reading. and you can get today's date by doing something like today=$(date +%s)

If you're happy w/ an awk & bash solution rather than doing the hard lifting in a loop:
awk -F: -v today=$(( $( date "+%s" ) / 86400 )) '$8!=""{print $1, today-$8}' /etc/shadow
-F: defines the input field separator to be a colon.
-v today=$(( $( date "+%s" ) / 86400 )) expresses today's date in days since epoch (which is the format used in /etc/shadow) and assigns it to an awk variable called today.
Now for the awk logic:
$8!="" if the 8th field (Account Expiry) isn't unset, {print $1, today-$8} print the username and the difference between today and the expiry date in days. If the date lies in the past, you get a positive value, if it's in the future, a negative one.
Looking at the jumble above it appears that you're trying to check for both password and account expiry:
This should do:
awk -F: -v today=$(( $( date "+%s" ) / 86400 )) '$3!="" || $8!=""{printf "%s\tpw: %s\tacc: %s\n", $1,today - $3, today-$8}' /etc/shadow
The 2nd field now shows the age of the password, the 3rd the account expiration. If this still isn't what you're after you'll need to sit down and rephrase your question so it reflects what you're actually after.


How to split a output of a command into an array in shell scripting?

I have executed a command that generated data in the form
I have defined a variable in the command and just echo that variable which procedure this data
now I want to store this data into an array, in shell scripting
so on
like this
I have tried a couple of things but all of them are storing index[0]. I want to perform some arithmetic operations on this data that why I want it into an array form.
select sum(a1) sum , date(create_timestamp) date from student where date(create_timestamp) >= 'YYYY-MM-DD' and date(create_timestamp) <= 'YYYY-MM-DD' group by date" | sed '1d'| while read sum date; do
# echo $sum
# echo $date
my_array=( "$sum" )
echo $my_array
# echo ${my_array[0]}
I think you mean my_array+=( "$sum" ) to add elements rather than overwrite the whole array, but if you trust the output format, why not assign it all at once instead of using a read loop?
mapfile -t my_array < <(
select sum(a1) sum , date(create_timestamp) date
from student
where date(create_timestamp) >= 'YYYY-MM-DD'
and date(create_timestamp) <= 'YYYY-MM-DD'
group by date" | sed '1d'

Number of Mondays Falls on the First of the month

I want a command line can display number of Monday(s) which fall(s) on the first of the month in a given year without using sed or awk commands
I have this command that display the first date of the current month
date -d "-0 month -$(($(date +%d)-1)) days"
With GNU date, you can read input from a file (or standard input):
printf '%s\n' 2021-{01..12}-01 | date -f- +%u | grep -c 1
This prints dates for the first of each month in a year, then formats them as "weekday" (where 1 is "Monday"), then counts the number of Mondays.
To parametrize the year, replace 2021 with a variable containing the year; wrapped in a function:
mondays() {
local year=$1
printf '%s\n' "$year"-{01..12}-01 | date -f- +%u | grep -c 1
Using a for loop, this can be accomplished as follows.
for mon in {01..12}; do date -d "2021-$mon-01" +%u; done | grep -c 1
We iterate through the numbers 01 to 12 representing the months.
We call date passing in the custom date value with the first date of each month in the year. We use +%u to return the day of week where 1 represents Monday.
Lastly we count the number of 1s using grep -c or grep --count
Note, the desired year has been hard coded as 2021. The current year can be used as:
for mon in {01..12}; do date -d "$(date +%Y)-$mon-01" +%u; done | grep -c 1
This can also all be put into a function and the desired year passed in as an argument:
getMondays() {
for mon in {01..12}; do date -d "$1-$mon-01" +%u; done | grep -c 1
I implemented it as:
for ((i=1,year=2021,mondays=0; i< 12; i++)) {
if [ $(date -d "$i/1/$year" +%u) -eq 1 ]
let "mondays++"
echo "There are $mondays Mondays in $year."
That said, I like Mushfiq's answer. Quite elegant.

Find the next nearest value (bash)

Let's say I have some holiday data (holiday_master.csv) in columns, something like
20200320 Vernal Equinox Day
20200429 Showa Day
20200503 Constitution Day
20200505 Green Day
20200720 Children's Day
20200811 Sea Day
Given this set of data, I want to find the next closest holiday from the given date.
For example if the input is 20200420, 20200429 Showa Day is expected.
If the input is 20200620, 20200720 Children's Day is expected.
I have a feeling that awk has the necessary functionality to do this, but any solution that works in a bash script is welcome.
Would you please try the bash script:
input="20200428" # or assign to whatever
< "holiday_master.csv" sort -nk1,1 | # sort the csv file by date and pass to the while loop
while read -r date desc; do
if (( date >= input )); then # if the date is greater than or equal to the input
echo "$date" "$desc" # then print the line
break # and exit the loop
Assuming no two days will ever have the same date...
DATE=<some desired input date>
awk "{print (\$1 - $DATE"' "\t" $0)}' calendar.txt | sed '/^-/d' | sort | head -n 1 | awk '{$1=""; print $0}'
awk "{print (\$1 - $DATE"' "\t" $0)}' calendar.txt: Prepend a column to the input.txt file describing the difference between the desired input date and the date column
sed '/^-/d': Remove all lines beginning with -. Dates with negative differences have already passed.
sort: Sort the remaining entries from least to greatest (based upon the difference column)
head -n 1: Select only the first row (The lowest difference)
awk '{$1=""; print $0}': Print all but the first column
Prettier script version
# Usage: script <Date> <Calendar file>
# Arg check and execute
if[ ! -f $CAL ]
echo "File not found: $CAL"
echo "Usage: script <Date> <Calendar file>"
elif [ $DATE -le 0 ]
echo "Invalid date: $DATE"
echo "Usage: script <Date> <Calendar file>"
elif [ $(echo "$DATE" | grep -Ewo -- '-?[0-9]+' | wc -l) -eq 0 ]
echo "Invalid date: $DATE"
echo "Usage: script <Date> <Calendar file>"
awk '{print ($1 - '"$DATE"' "\t" $0)}' $CAL | sed '/^-/d' | sort | head -n 1 | awk '{$1=""; print $0}'
As you use YYYYMMDD format we might compare it just like numbers (note: year is greater than month, month is greater than day). So you can use AWK following way, let:
20200320 Vernal Equinox Day
20200429 Showa Day
20200503 Constitution Day
20200505 Green Day
20200720 Children's Day
20200811 Sea Day
be file named holidays.txt then:
awk 'BEGIN{inputdate=20200420}{if($1>inputdate){print $2;exit}}' holidays.txt
Explanation: in BEGIN I set inputdate to 20200420 then when line with greater number in 1st column is found I print content of 2nd column and exit (otherwise later dates would be printed too). Note that AWK does automatically parse number when asked to do comparison (> in this case) so you do not have to care about conversion yourself - you could even do inputdate="20200420" and it would work too.
This solution assumes that all dates in file are already sorted.
Using awk and assuming the source data is comma separated:
awk -F, -v dayte="20200420" '
"date -d "dayte" +%s" | getline dat1
"date -d "$1" +%s" | getline dat2;
if (dat3 > 0 )
print hols[hols1[1]]
' holiday_master.csv
One liner:
awk -F, -v dayte="20200420" 'BEGIN { "date -d "dayte" +%s" | getline dat1 } { "date -d "$1" +%s" | getline dat2;dat3=dat2-dat1;if (dat3 > 0 ) { hols[dat3]=$2 } } END { asorti(hols,hols1,"#ind_num_asc");print hols[hols1[1]] }' holiday_master.csv
Set the field separator to , and set a variable dayte to the date we wish to check. In the BEGIN block, we pass the dayte variable through to date command via an awk pipe/getline and read the epoch result into the variable dat1. We do the same with the first column on the master file ($1) and read this into dat2. We take the difference between the epoch dates and read the result into dat3. Only if the result is positive (in the future) do we then use dat3 for an index in a "hols" array, with the holiday description as the value. In the END block, we sort the indexes of hols into a news hols1 array basing the sort on ascending, numeric indexes. We then take the first index of the new hols1 array to attain the holiday that is closest to the dayte variable.
Assuming the holiday list file is sorted by date as you have given, the below would work
$ awk -v dt="20200420" ' (dt-$1)<0 { print;exit } ' holiday.txt
20200429 Showa Day
$ awk -v dt="20200620" ' (dt-$1)<0 { print;exit } ' holiday.txt
20200720 Children's Day
If the holiday file is not sorted, then you can use below
$ shuf holiday.txt | awk -v dt="20200420" ' dt-$1<0 { a[(dt-$1)*-1]=$0 } END { asort(a); print a[1] } '
20200429 Showa Day
$ shuf holiday.txt | awk -v dt="20200620" ' dt-$1<0 { a[(dt-$1)*-1]=$0 } END { asort(a); print a[1] } '
20200720 Children's Day

Using awk to add one month to a date [duplicate]

This question already has answers here:
Increment date with AWK for few days and months
(3 answers)
Closed 4 years ago.
I have a file 1.txt like below:
"15227962157615645"$"2018-12-04 06:55:43"
"15227525816721347"$"2018-12-03 18:48:11"
I can get the date using:
awk -F\" '{print $4}' 1.txt
Additionally I need add one month to the date. For the above input my desired output would be:
2019-01-04 06:55:43
2019-01-03 18:48:11
I tried to use
awk -F\" '{print date -d "$4 +1 month"+%Y-%m-%d}' 1.txt
but it does not work.
Awk has limited support for date calculation, so here is a bash only solution relying on the date command:
while read n t; do
printf '%s$"%s"\n' "$n" "$(date -d "${t//\"/} +1 month" '+%F %T')"
done <file
The input field separator is set to $ to get the time into $t variable.
The double quote of the date field are removed using bash parameter expansion ${t//\"/}.
This allows to pass the +1 month key word to date.
Then the printf prints back to the original format of the input file.

Filter Linux logs based on Unix Timestamp

I have a log on a linux server. The entries are in the format:
[timestamp (seconds since jan 1 1970)] log data entry
I need a bash script that will take the name of the log file and output only yesterdays entries (from 12:00 to 23:59:59 of previous day) and output those lines to a new file.
I've seen various scripts that filter logs based on dates but all of them so far deal with date stamps in more human readable formats, or are not dynamic. They rely on hard coded dates. I want a script that is going to run in a cron job daily so it has to be aware of what the current date is each time it runs.
Update: This is what I have so far. It just never seems to do the evaluation of the date. It prints 00 for the date so everything gets through.
head -5 logfile.log | awk '{
if($1 >= (date -d "today 00:00:00" +"%s"))
print $1 (date -d "today 00:00:00" +"%s");
I'm confused though, even if the date evaluates properly, $1 is going to have numbers inside square brackets, and my date will be just numbers. Will it do the comparison properly if the strings are formatted differently like that? I haven't figured out how to shove the date number returned by date into a string with brackets yet.
Well, maybe using the dates as Dale said. But using a little trick to extract the "[" and "]", and after compare the dates. Something like this:
YESTERDAY=$(date -d "yesterday 00:00:00" +"%s")
TODAY=$(date -d "today 00:00:00" +"%s")
# Combine the processing in awk
awk -v MIN=${YESTERDAY} -v MAX=${TODAY} -F["]""["] '{ if ( $2 >= MIN && $2 <= MAX) print $0}' logfile.log
Combining tips and tricks from Glenn, Dale, and Davison:
awk -v today=$(date -d "today 00:00:00" +"%s") -v yesterday=$(date -d "yesterday 00:00:00" +"%s") -F'[\\[\\] ]' '{ if($2 >= yesterday && $2 < today) print }' logfile.log
Uses the shell's $() command substitution to feed variables to awk's -v argument parser
-F'[\\[\\] ]' sets the field separator to be either [, ], or
input data:
[1300000000 log1 data1 entry1]
[1444370000 log2 data2 entry2]
[1444374000 log3 data3 entry3]
[1444460399 log4 data4 entry4]
[1500000000 log5 data5 entry5]
[1444370000 log2 data2 entry2]
You might try something like this:
YESTERDAY=$(date -d "yesterday 00:00:00" +"%s")
TODAY=$(date -d "today 00:00:00" +"%s")
cat your_log.log | \
awk -v MIN=${YESTERDAY} -v MAX=${TODAY} \
'{if($1 >= MIN && $1 < MAX) print}'