Extract password expire date - linux

I want to get the password expire date from this output:
>chage -l dsi
Last password change : Feb 05, 2020
Password expires : May 05, 2020
Password inactive : never
Account expires : never
Minimum number of days between password change : 0
Maximum number of days between password change : 90
Number of days of warning before password expires : 7
I already got so far that i only see the both dates for "last password change" and "Password expires", but i don´t get it working to only get the "password expires" date.
>chage -l dsi | cut -d ':' -f2 | head -n 2
Feb 05, 2020
May 05, 2020
How can I only get "May 05, 2020" to store it in a variable for further processing?
Thanks.

There are several options. The one that follows your schema could be:
chage -l dsi | cut -d ':' -f2 | head -n 2 | tail -1
So you get the first two lines and then the last line (which is the second in the whole text).
I don't like this approach, as it is completely dependent on the position of the information. If the order changes, you will get a wrong answer. I would go for searching the piece of information you need and then extracting the date:
chage -l dsi | grep "Password expires" | cut -d ':' -f2

You could use grep to filter the line you want before parsing the output:
chage -l dsi | grep "Password expires" | cut -d ':' -f2
That way you always get only the row you want.

With awk:
chage -l dsi | awk -F':' '$1 ~ /^Password expires/{ print $2 }'
or if you want to get rid of the space character after the colon:
chage -l dsi | awk -F':' '$1 ~ /^Password expires/{ sub(/^[[:blank:]]/, "", $2); print $2 }'

And a variant with sed
chage -l dsi | sed -n '/Password expires/s/^.*: //p'

Related

Remove first n "words" from string variable in Bash

I want to remove the first 4 words from my string variable "DATES".
Does someone have a simple solution for this?
Here my example:
DATES="31 May 2021 10:22:01 30 May 2021 10:23:01 29 May 2021 10:24:01"
WC=$(echo $DATES | wc -w)
DATE_COUNT=$(( $WC / 4 - 1 ))
for i in {0..$DATE_COUNT}
do
YEAR=$(echo $DATES | awk '{print $3}')
MONTH=$(echo $DATES | awk '{print $2}')
MONTH=$( date --date="$(printf "01 %s" $MONTH)" +"%m")
DAY=$(echo $DATES | awk '{print $1}')
TIME=$(echo $DATES | awk '{print $4}' | sed 's/://g')
DATE_ARRAY[$i]="$YEAR$MONTH$DAY$TIME"
#Remove first 4 words from string
done
Use cut.
DATES="31 May 2021 10:22:01 30 May 2021 10:23:01 29 May 2021 10:24:01"
echo $DATES | cut -d' ' -f 5-
Output:
30 May 2021 10:23:01 29 May 2021 10:24:01
You can even use it for a cleaner solution than awk, like this:
YEAR=$(echo $DATES | cut -d' ' -f 3)
General version to remove n first words
remove_n_first_words(){
echo $2 | cut -d' ' -f $(($1+1))-
}
remove_n_first_words 4 "$DATES"
Using bash regex operator =~:
$ [[ $DATES =~ ^(([^ ]+ +){4})(.*) ]] && echo ${BASH_REMATCH[3]}
30 May 2021 10:23:01 29 May 2021 10:24:01
Maybe use read ?
DATES="31 May 2021 10:22:01 30 May 2021 10:23:01 29 May 2021 10:24:01"
read -ra dates <<< "$DATES"; echo "${dates[#]:4}"
Or just store the data in an array directly.
DATES=(31 May 2021 10:22:01 30 May 2021 10:23:01 29 May 2021 10:24:01)
echo "${DATES[#]:4}"
To get the total words/elements like with wc -c
echo "${#DATES[*]}"

How to generate a log record in a comma separated format

Earlier I am using this command which generates a record in the space-separated format
sudo cat path of the file |
awk -v d="$(date --date="1 days ago" +"%h %e")" '
/Accepted/ && ($0 ~ d) { print $1,$2,$9,$11}' |
sort | uniq -c
like this
xx xx xx xxxx xxx
xx xx xx xxxx xxx
Now my aim is to generate a comma-separated record for that I'm using this way
sudo cat path of the file |
awk -v d="$(date --date="1 days ago" +"%h %e")" '
/Accepted/ && ($0 ~ d) { print $1","$2","$9","$11}' |
sort | uniq -c
but it is giving me the record like this
xx xx,xx,xxxx,xxx
xx xx,xx,xxxx,xxx
I want to achieve a comma after the first column so on.
How can I do it help, please
The problem is that uniq -c prefixes the count to the line, separating the count from the data with a space (and usually including spaces before the count, at least for counts up to 3 digits long on a Mac, 6 digits long on RHEL 7.4). So, if there is no way to change the separator (there isn't on a Mac; and uniq 8.22 on RHEL 7.4 does not include such an option), then you'll have to do it yourself.
However, you can use awk to do the counting and formatting, leaving an optional sort for post-processing:
sudo cat /var/log/secure |
awk -v d="$(date --date="1 days ago" +"%h %e")" '
/Accepted/ && ($0 ~ d) { key = $1 "," $2 "," $9 "," $11; count[key]++ }
END { for (key in count) print count[key] ",", key }'
Warning: untested code — there isn't any usable sample data in the question!
I have used this if anyone has a better approach then do tell me
sudo cat path of the file |
awk -v d="$(date --date="1 days ago" +"%h %e")" '
/Accepted/ && ($0 ~ d) { print $1,$2,$9,$11}' |
sort | uniq -c | sed -E -e 's/^[[:blank:]]+//g' -e 's/[[:blank:]]+/,/g'
I would suggest to use "sed" and replace whitespace with ,. Here is simple echo statement to do that
[user#hostname ~]$ echo "xx xx xx xxxx xxx" | sed 's/ /,/g'
xx,xx,xx,xxxx,xxx
Similarly you can use another pipe at end and do sed something like
sudo cat path of the file |
awk -v d="$(date --date="1 days ago" +"%h %e")" '
/Accepted/ && ($0 ~ d) { print $1,$2,$9,$11}' |
sort | uniq -c | sed 's/ /,/g'

Print out only last 4 digits of mac addresses from 2nd column using awk in linux

I have made a shell script for getting the list of mac address using awk and arp-scan command. I want to strip the mac address to only last 4 digits i.e (i want to print only the letters yy)
ac:1e:04:0e:yy:yy
ax:8d:5c:27:yy:yy
ax:ee:fb:55:yy:yy
dx:37:42:c9:yy:yy
cx:bf:9c:a4:yy:yy
Try cut -d: -f5-
(Options meaning: delimiter : and fields 5 and up.)
EDIT: Or in awk, as you requested:
awk -F: '{ print $5 ":" $6 }'
here are a few
line=cx:bf:9c:a4:yy:yy
echo ${line:(-5)}
line=cx:bf:9c:a4:yy:yy
echo $line | cut -d":" -f5-
I imagine you want to strip the trailing spaces, but it isn't clear whether you want yy:yy or yyyy.
Anyhow, there are multiple ways to it but you already are running AWK and have the MAC in $2.
In the first case it would be:
awk '{match($2,/([^:]{2}:[^:]{2}) *$/,m); print m[0]}'
yy:yy
In the second (no colon :):
awk 'match($2,/([^:]{2}):([^:]{2}) *$/,m); print m[1]m[2]}'
yyyy
In case you don't have match available in your AWK, you'd need to resort to gensub.
awk '{print gensub(/.*([^:]{2}:[^:]{2}) *$/,"\\1","g",$2)}'
yy:yy
or:
awk '{print gensub(/.*([^:]{2}):([^:]{2}) *$/,"\\1\\2","g",$0)}'
yyyy
Edit:
I now realized the trailing spaces were added by anubhava in his edit; they were not present in the original question! You can then simply keep the last n characters:
awk '{print substr($2,13,5)}'
yy:yy
or:
awk '{print substr($2,13,2)substr($2,16,2)}'
yyyy
Taking into account that the mac address always is 6 octets, you probably could just do something like this to get the last 2 octets:
awk '{print substr($0,13)}' input.txt
While testing on the fly by using arp -an I notice that the output was not always printing the mac addresses in some cases it was returning something like:
(169.254.113.54) at (incomplete) on en4 [ethernet]
Therefore probably is better to filter the input to guarantee a mac address, this can be done by applying this regex:
^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$
Applying the regex in awk and only printing the 2 last octecs:
arp -an | awk '{if ($4 ~ /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/) print substr($4,13)}'
This will filter the column $4 and verify that is a valid MAC address, then it uses substr to just return the last "letters"
You could also split by : and print the output in multiple ways, for example:
awk '{if ($4 ~ /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/) split($4,a,":"); print a[5] ":" a[6]}
Notice the exp ~ /regexp/
This is true if the expression exp (taken as a string) is matched by regexp.
The following example matches, or selects, all input records with the upper-case letter `J' somewhere in the first field:
$ awk '$1 ~ /J/' inventory-shipped
-| Jan 13 25 15 115
-| Jun 31 42 75 492
-| Jul 24 34 67 436
-| Jan 21 36 64 620
So does this:
awk '{ if ($1 ~ /J/) print }' inventory-shipped

print time in double decimal shell script

how can I print output in double decimal.
below command will print hour in GMT format but i want output as 06 and for double digit hour it should be 10,11,12.
date -u --date="today" +"%I" | awk -F' ' '{print $1-1}'
6
You may use printf "%02d" in awk to achieve it,
$ date -u --date="today" +"%I" | awk -F' ' '{printf "%02d\n",$1-1}'
06
Perhaps you want:
date -u --date="- 1 hour" +"%I"`
If the time adjustment is part of your date string, the format will not be munged.
Alternately, if what you really want is a way to zero-pad a number in bash or awk, you have a variety of alternatives:
date -u --date="- 1 hour" +"%I" | awk '{printf "%02d\n",$1-1}'
Or in bash alone:
read hour < <( date -u --date="- 1 hour" +"%I" )
printf '%02d\n' "$hour"
Get the idea? Output format happens when you print your output, and printf in whatever language formats your output.
awk is superfluous here. You can use a relative time format with date:
date -u --date="-1 hour" +"%I"
06

tr "[1-9]" "['01'-'09']" not working properly

I'm trying to cut only the date part from a ls -lrth | grep TRACK output:
-rw-r--r-- 1 ins ins 0 Dec 3 00:00 TRACK_1_20121203_01010014.LOG
-rw-r--r-- 1 ins ins 0 Dec 3 00:00 TRACK_0_20121203_01010014.LOG
-rw-r--r-- 1 ins ins 0 Dec 13 15:10 TRACK_9_20121213_01010014.LOG
-rw-r--r-- 1 ins ins 0 Dec 13 15:10 TRACK_8_20121213_01010014.LOG
But, doing this:
ls -lrth | grep TRACK | tr "\t" " " | cut -d" " -f 9
only gives me the dates which are double digits and spaces for single digits:
13
13
So I tried something with tr command, to translate all single digit dates to double digits:
ls -lrth | grep TRACK | tr "\t" " " | tr "[1-9]" "['01'-'09']" | cut -d" " -f 9
But it's giving some weird results, and evidently don't serve my purpose. Any ideas on how to get the correct output?
Don't parse ls output.
ls is a tool for interactively looking at file information. Its output is formatted for humans and will cause bugs in scripts. Use globs or find instead. Understand why: http://mywiki.wooledge.org/ParsingLs
I recommend this way :
If you want the date and the file path :
find . -name 'TRACK*' -printf '%a %p\n'
If you want only the date:
find . -name 'TRACK*' -printf '%a\n'
You could try another approach with something like
find . -name 'TRACK*' -exec stat -c %y {} \; | sort
You can add something like | cut -f1 -d' ' if you only need the date.
I guess this does suffice:
ls -lhrt | grep TRACK | awk '{print $6, $7, $8}'
that kind of substitution would be better handled through sed:
ls -lrth | grep TRACK | sed 's/ \+/ /g;s/ \([0-9]\) / 0\1 /g' | cut -d" " -f 7
As already said, never parse the output of ls!
Since you only want the modification time, the command date has a cool option for that: option -r (man date for more info).
Hence, you probably want this instead of your line:
for i in TRACK*; do date -r "$i"; done
I don't know how you want the format of the date, so play with the options, e.g.,
for i in TRACK*; do date -r "$i" "+%D"; done
(the formats are in man date).
Use stat to get information about a file.
Also, tr only does one-to-one character translation. It won't replace one-character sequences with two-character ones.

Resources