Getting a user's log-in date from who command - bash

I've been trying to get only the date a user logged in in bash.
I tried using
who | grep 'user'
which gives me the data for the user just fine. I've been unsuccessful at getting a string containing the date the user logged in, however:
who | grep 'user' | cut -f3
doesn't work for some reason.
I tried to use a space as a delimiter, but which field to cut would vary depending on the length of the username:
who | grep 'user' | cut -d ' ' -f12
would give me the date for usernames of a fixed length, but would give me a different field for usernames of different lengths.
It looks like the delimiter is a tab as the number of spaces between the first and second fields changes depending on the length of the username. I tried specifying the delimiter as a tab (even though cut assumes it is), but that didn't work either.

Try using awk instead of cut, you can even get awk to do the filtering grep is currently doing:
who | awk '/user/{print $3}'
The use of $3 means that the 3rd column of the data will be output. Awk's default field separator is a sequence of whitespaces.
Using awk to replace only your cut command (and not grep) would result in:
who | grep 'user' | awk '{print $3}'

Use awk or some other tool less bothered by multiple delimiters than cut.
who | awk '{print $3}'

Related

Extracting UUID string from a text using shell script commands

I have a text line as:
host_id -------------------------------------- f5f4d3e7-fdc4-49f2-b32c-13280bc7db66 (1 rows)
And I want to extract only the UUID value within the text using shell script as I need to run this on ubuntu to execute some other commands using the value. I tried using cut command:
cut -d '-' -f2 | cut -d '(' -f1
But couldn't succeed to specify the cut character for before and after of the UUID string.
The problem using cut -d'-' in your case is that the field (-f) values you will have to add is not 2 but 38 and above (that's the number of consecutive - symbols in your string), because of how cut works. This is the output you will get if you just set the field 39 (using cut -d'-' -f39):
f5f4d3e7
But also the UUID string you want contains additional hyphens, so if you still want to get it that way (using cut) you will have to expand your field selection, in this case by adding -f39-43. But that just will not solve the problem of showing the string "(1 rows)" after it, so by running cut -d'-' -f39-43 you will get this:
f5f4d3e7-fdc4-49f2-b32c-13280bc7db66 (1 rows)
And that's why you used a second cut filter to just pick the UUID value string. While this is okay, it seems to be pretty much handy using awk, which will solve all these problems in one single command:
cat file.txt | awk '{ print $3 }'
Or if your string is not coming from a file:
echo "host_id -------------------------------------- f5f4d3e7-fdc4-49f2-b32c-13280bc7db66 (1 rows)" | awk '{ print $3 }'

How to grep only the first string in a line

I'm writing a script that checks a list of all the users connected to the server (using who) and writes to the file Information the list of usernames of only those having letters a, b, c or d. This is what I have so far:
who | grep '[a-d]' >> Information
However, the command who displays this:
username pts/148 2019-01-29 16:09 (IP address)
What I don't understand is why my grep search is also displaying the pts/148, date, time, and IP address. I just want it to send the username to the file Information.
Any help is appreciated.
Another way is to use the command cut to get the first part of the string only.
who | cut -f 1 -d ' ' | grep '[a-d]' >> Information
Using awk to output records where the first clumn matches [a-d]:
$ who | awk '$1~/[a-d]/' >> Information
Using grep to search for lines with [a-d] before the first space:
$ who | grep -o "^[^ ]*[a-d][^ ]*" >> Information
You need to get the first word, otherwise grep will display the entire line that has the matching text. You could use awk:
who | awk '{ if (substr($1,1,1) ~ /^[a-d]/ ) print $1 }' >>Information

Substring in linux using cut

I would like to grab a substring of a file to get the default password of mysql in centos.
This is the command I am using to get the password:
sudo grep 'temporary password' /var/log/mysqld.log
which result is:
2018-02-21T07:03:11.681201Z 1 [Note] A temporary password is generated for root#localhost: >KkHAt=#z6OV
Now, I am using this command to get the password only and remove the unnecessary stuff, so I can use it in a script:
sudo grep 'temporary password' /var/log/mysqld.log | cut -d ':' -f 4 | cut -d ' ' -f 2
But using 2 cuts seems very ugly. Is there another command or tool that I can use, or a more elegant way to do this?
Using awk:
$ awk '/temporary password/{print $NF}' file
>KkHAt=#z6OV
Bearing in mind that awk splits the lines in fields based on a field separator (by default whitspaces) and NF refers to the number of fields, you can print the last field with:
$ grep 'temporary password' /var/log/mysqld.log | awk '{print $NF}'

shell script to extract text from a variable separated by forward slashes

I am trying to find a way to to extract text from a variable with words separated by a forward slash. I attempted it using cut, so here's an example:
set variable = '/one/two/three/four'
Say I just want to extract three from this, I used:
cut -d/ -f3 <<<"${variable}"
But this seems to not work. Any ideas of what I'm doing wrong? Or is there a way of using AWK to do this?
You need to remove the spaces before and after to = during string or variable assignment. And tell the cut command to print the 4th field.
$ variable='/one/two/three/four'
$ cut -d/ -f4 <<<"${variable}"
three
With the delimiter /, cut command splits the input like.
/one/two/three/four
| | | | |
1 2 3 4 5
that is, when it splits on first slash , you get an empty string as first column.
I think that the main problem here is in your assignment. Try this:
var='/one/two/three/four'
cut -d/ -f4 <<<"$var"
Here is an awk version:
awk -F\/ '{print $4}' <<< "$variable"
three
or
echo "$variable" | awk -F\/ '{print $4}'
three
PS to set a variable not need for set and remove spaces around =
variable='/one/two/three/four'

Using sed/awk to limit/parse output of LDAP DN's

I have a large list of LDAP DN's that are all related in that they failed to import into my application. I need to query these against my back-end database based on a very specific portion of the CN, but I'm not entirely sure on how I can restrict down the strings to a very specific value that is not necessarily located in the same position every time.
Using the following bash command:
grep 'Failed to process entry' /var/log/tomcat6/catalina.out | awk '{print substr($0, index($0,$14))}'
I am able to return a list of DN's similar to: (sorry for the redacted nature, security dictates)
"cn=[Last Name] [Optional Middle Initial or Suffix] [First Name] [User name],ou=[value],ou=[value],o=[value],c=[value]".
The CN value can be confusing as the order of surname, given name, middle initial, prefix or suffix can be displayed in any order if the values even exist, but one thing does remain consistent, the username is always the last field in the cn (followed by a "," then the first of many potential OU's). I need to parse out that user name for querying, preferably into a comma separated list for easy copy and paste for use in a SQL IN() query or use in a bash script. So as an example, imagine the following short list of abbreviated DNs, only showing the CN value (since the rest of the DN is irrelevant):
"cn=Doe Jr. John john.doe,ou=...".
"cn=Doe A. Jane jane.a.doe,ou=...".
"cn=Smith Bob J bsmith,ou=...".
"cn=Powers Richard richard.powers1,ou=...".
I would like to have a csv list returned that looks like:
john.doe,jane.a.doe,bsmith,richard.powers1
Can a mix of awk and/or sed accomplish this?
sed -e 's/"^[^,]* \([^ ,]*\),.*/\1/'
will parse the username part of the common name and isolate the username. Follow up with
| tr '\n' , | sed -e 's/,$/\n/'
to convert the one-per-line username format into comma-separated form.
Here is one quick and dirty way of doing it -
awk -v FS="[\"=,]" '{ print $3}' file | awk -v ORS="," '{print $NF}' | sed 's/,$//'
Test:
[jaypal:~/Temp] cat ff
"cn=Doe Jr. John john.doe,ou=...".
"cn=Doe A. Jane jane.a.doe,ou=...".
"cn=Smith Bob J bsmith,ou=...".
"cn=Powers Richard richard.powers1,ou=...".
[jaypal:~/Temp] awk -v FS="[\"=,]" '{ print $3}' ff | awk -v ORS="," '{print $NF}' | sed 's/,$//'
john.doe,jane.a.doe,bsmith,richard.powers1
OR
If you have gawk then
gawk '{ print gensub(/.* (.*[^,]),.*/,"\\1","$0")}' filename | sed ':a;{N;s/\n/,/}; ba'
Test:
[jaypal:~/Temp] gawk '{ print gensub(/.* (.*[^,]),.*/,"\\1","$0")}' ff | sed ':a;{N;s/\n/,/}; ba'
john.doe,jane.a.doe,bsmith,richard.powers1
Given a file "Document1.txt" containing
cn=Smith Jane batty.cow,ou=ou1_value,ou=oun_value,o=o_value,c=c_value
cn=Marley Bob reggae.boy,ou=ou1_value,ou=oun_value,o=o_value,c=c_value
cn=Clinton J Bill ex.president,ou=ou1_value,ou=oun_value,o=o_value,c=c_value
you can do a
cat Document1.txt | sed -e "s/^cn=.* \([A-Za-z0-9._]*\),ou=.*/\1/p"
which gets you
batty.cow
reggae.boy
ex.president
using tr to transalate the end of line character
cat Document1.txt | sed -n "s/^cn=.* \([A-Za-z0-9._]*\),ou=.*/\1/p" | tr '\n' ','
produces
batty.cow,reggae.boy,ex.president,
you will need to deal with the last comma
but if you want it in a database say oracle for example, a script containing:
#!/bin/bash
doc=$1
cat ${doc} | sed -e "s/^cn=.* \([A-Za-z0-9._]*\),ou=.*/\1/p" | while read username
do
sqlplus -s username/password#instance <<+++ insert into mytable (user_name) values ('${username}'\;)
exit
+++
done
N.B.
The A-Za-z0-9._ in the sed expression is every type of character you expect in the username - you may need to play with that one.
caveat - I did't test the last bit with the database insert in it!
Perl regex solution that I consider more readable than the alternatives, in case you're interested:
perl -ne 'print "$1," if /(([[:alnum:]]|[[:punct:]])+),ou/' input.txt
Prints the string preceding 'ou', accepts alphanumeric and punctuation chars (but no spaces, so it stops at the username).
Output:
john.doe,jane.a.doe,bsmith,
It has been over a year since there has been an idea posted to this, but wanted a place to refer to in the future when this class of question comes up again. Also, I did not see a similar answer posted.
Of the pattern of data provided, my interpretation is that we can strip away everything after the first comma, leaving us with a true CN rather than a DN that starts with a CN.
In the CN, we strip everything before and including the last white space.
This will leave us with the username.
awk -F',' /^cn=/{print $1}' ldapfile | awk '{print $NF}' >> usernames
Passing your ldap file to awk, with the field separator set to comma, and the match string set to cn= at the beginning of a line, we print everything up to the first comma. Then we pipe that output into an awk with the default field separator and print only the last field, resulting in just the username. We redirect and append this to a file in the current directory named usernames, and we end up with one username per line.
To convert this into a single comma separated line of usernames, we change the last print command to printf, leaving out the \n newline character, but adding a comma.
awk -F',' /^cn=/{print $1}' ldapfile | awk '{printf $NF","}' >> usersnames
This leaves the only line in the file with a trailing comma, but since it is only intended to be used for cut and paste, simply do not cut the last character. :)

Resources