bash script grep the etc file for user information - bash

I am trying to write a script in bash to read in arguments (usernames) and then return information about that username by searching the /etc/passwd file. I am not the root on the server that I am scripting this on, but I don't need to be. Here is the code that I have so far, but I dont think I am using the grep command right.
#!/bin/bash
if [ $# -eq 1 ]; then #if num of args =1; then continue on
UNAME=$1 #set arg to var UNAME
grep UNAME=$(grep $/etc/passwd)
if [ $UNAME == /etc/passwd ]; then #if UNAME exists then display info below
echo "-------------------------------------------"
echo "Username: $(grep UNAME/etc/passwd/ $f1) "
echo "User ID (UID): $(grep UNAME/etc/passwd/) "
echo "Group ID (GID): $(grep UNAME/etc/passwd/) "
echo "User info: $(grep UNAME/etc/passwd/) "
echo "Home directory: $(grep UNAME/etc/passwd/) "
echo "Command shell: $(grep UNAME/etc/passwd/) "
echo "-------------------------------------------"
else #if UNAME is nonexistant then display this error message
echo "-------------------------------------------"
echo ""$UNAME" does not exist."
echo "-------------------------------------------"
fi
fi
if [ $# -eq 0 ]; then #if num of args =0; then display this
echo "Not enough arguments provided."
echo "USAGE: $0 <user_name> [user_name] ..."
exit 1
fi

You're doing too much. grep is the wrong tool for the type of parsing you are trying to do. The solution given here is pretty sloppy since it reads the passwd file once for each user you are querying instead of reporting everything in one pass, but it's not unreasonable:
#!/bin/sh
for UNAME; do
while IFS=: read login passwd uid gid name home shell; do
if test "$login" = "$UNAME"; then
echo "-------------------------------------------"
echo "Username: $login"
echo "User ID (UID): $uid"
echo "Group ID (GID): $gid"
echo "User info: $name"
echo "Home directory: $home"
echo "Command shell: $shell"
echo "-------------------------------------------"
continue 2
fi
done < /etc/passwd
echo "No entry for user $UNAME" >&2
done

Before I get to the explanation, here is how I would have written your script:
cat ./test.sh
#!/bin/bash
UNAME=${1} #set arg to var UNAME
if [ -z ${UNAME} ]; then #if no argument is provided; then display this
echo "Not enough arguments provided."
echo "USAGE: $0 <user_name>"
exit 1
fi
if grep ${UNAME} /etc/passwd >/dev/null; then #if UNAME exists then display info below
echo "-------------------------------------------"
echo "Username: $(awk -F ':' -v uname=${UNAME} '$0 ~ uname {print $1}' /etc/passwd)"
echo "User ID (UID): $(awk -F ':' -v uname=${UNAME} '$0 ~ uname {print $3}' /etc/passwd)"
echo "Group ID (GID): $(awk -F ':' -v uname=${UNAME} '$0 ~ uname {print $4}' /etc/passwd)"
echo "User info: $(awk -F ':' -v uname=${UNAME} '$0 ~ uname {print $5}' /etc/passwd)"
echo "Home directory: $(awk -F ':' -v uname=${UNAME} '$0 ~ uname {print $6}' /etc/passwd)"
echo "Command shell: $(awk -F ':' -v uname=${UNAME} '$0 ~ uname {print $7}' /etc/passwd)"
echo "-------------------------------------------"
else #if UNAME is nonexistant then display this error message
echo "-------------------------------------------"
echo "\"${UNAME}\" does not exist."
echo "-------------------------------------------"
fi
Explanation:
if can use grep directly to check for the existence of
your argument in passwd
UNAME= is how arguments are assigned, not
called - they are called with the $ - for example $UNAME
/etc/passwd is an absolute file name and not an argument, thus
$/etc/passwd doesn't mean anything to bash
argument=$(any old bash command) is telling bash that you want to
assign the output of "any old bash command" to "argument", so $()
has to contain a command with output for "argument" to be assigned
anything.
the >/dev/null sends the output of grep to the waste basket because we only care at this point whether or not it's successful so that if knows what to do
grep works like this: grep <the thing you want to find> <file> (note that spaces matter and to use "" if you want to include spaces in your search)
to get specific fields, awk is more useful. awk -F':' tells awk that I want : to define my field separators; -v uname=${UNAME} passes my argument to awk; within awk, $0 ~ uname checks the line for my argument (like grep); and {print $1} prints the first field, and so on
Also consider the limitation of partial matches. If, for example, I have a user JSmith and Smith, searching Smith will return both. This can be addressed with regex but will add a great deal of complexity to the script.
Here is a version with lighter awk usage which works with multiple arguments:
#!/bin/bash
for username; do
if grep ${username} /etc/passwd >/dev/null; then #if username exists then display info below
echo "---------------------------------------"
awk -F':' -v uname="${username}" '\
$0 ~ uname \
{print " Username: "$1\
"\n User ID (UID): "$3\
"\n Group ID (GID): "$4\
"\n User info: "$5\
"\n Home directory: "$6\
"\n Command shell: "$7}' /etc/passwd
echo "---------------------------------------"
else #if username is nonexistant then display this error message
echo "---------------------------------------"
echo "\"${username}\" does not exist."
echo "---------------------------------------"
fi
done
if [ -z ${1} ]; then #if no argument is provided; then display this
echo " Not enough arguments provided."
echo " USAGE: $0 <user name>"
exit 1
fi
*Credit to William Pursell for explaining the for loop

The best tool to read databases such as passwd is getent. This will fetch entries from a database supported by Name Service Switch Libraries. If you really need to limit reading the files databases, you can tell getent to use files database with -s files.
Your code is unnecessarily long. Fetching a record and parsing them into multiple variables should be enough.
#!/bin/bash
if [ $# -eq 1 ]; then #if num of args =1; then continue on
if IFS=: read username ignore uid gid gecos home shell < <(getent -s files passwd $1); then
echo "-------------------------------------------"
echo "Username: $username"
echo "User ID (UID): $uid"
echo "Group ID (GID): $gid"
echo "User info: $gecos"
echo "Home directory: $home"
echo "Command shell: $shell"
echo "-------------------------------------------"
else #if $1 is nonexistant then display this error message
echo "-------------------------------------------"
echo "'$1' does not exist."
echo "-------------------------------------------"
fi
fi
if [ $# -eq 0 ]; then #if num of args =0; then display this
echo "Not enough arguments provided."
echo "USAGE: $0 <user_name> [user_name] ..."
exit 1
fi

Related

Read all lines except commented ones, lines containing 'bind|swap|shm' from/etc/fstab and print the ones in them that are not mounted?

Read all lines except commented ones, lines containing 'bind|swap|shm' from/etc/fstab and print the ones in them that are not mounted? I have tried the below and I was wondering if I could get a better shorter code.
mount_check()
{
fstb=$(cat /etc/fstab |egrep -vw 'bind|swap' |awk '$1 !~/#|^$/ {print $2}')
for i in ${fstb}
do
df -hPT | grep -wq ${i}
if [ $? -eq 1 ]
then
echo "The file system ${i} has an entry in /etc/fstab file but not mounted"
fi
done
}rc_mount_check=`mount_check |tee |wc -l`if [ $rc_mount_check -eq '0' ]
then
echo -e "OK: All file systems listed in /etc/fstab are mounted"
exit $OK
else
echo -e "CRITICAL: Please verify and mount the file systems\n$(mount_check)\n"
exit $CRITICAL
fi
Actual command which you are looking for is findmnt. Could you please try following.
awk '
!/bind|swap|shm/ && $1 !~/#|^$/{
system("if [[ -n $(findmnt -m " $2 ") ]]; \
then echo Mount " $2 " is mounted.;\
else echo Mount " $2 " is NOT mounted.;\
fi"\
)
}
' /etc/fstab
OR in form of one-liner try following(in case you need it):
awk '!/bind|swap|shm/ && $1 !~/#|^$/ {system("if [[ -n $(findmnt -m " $2 ") ]]; then echo Mount " $2 " is mounted.;else echo Mount " $2 " is NOT mounted.;fi")}' /etc/fstab
Assuming the currently accepted answer:
awk '
!/bind|swap|shm/ && $1 !~/#|^$/{
system("if [[ -n $(findmnt -m " $2 ") ]]; \
then echo Mount " $2 " is mounted.;\
else echo Mount " $2 " is NOT mounted.;\
fi"\
)
}
' /etc/fstab
does what you want, you should actually do something like this (untested) instead:
mounts=( $(awk '!/bind|swap|shm/ && $1 !~/#|^$/ { print $2 }' /etc/fstab) )
for mount in "${mounts[#]}"; do
if [[ -n $(findmnt -m "$mount") ]]
then echo "Mount $mount is mounted."
else echo "Mount $mount is NOT mounted."
fi
done
i.e. don't call shell to call awk to call system to call shell to call findmnt. Just have shell 1) call awk to get whatever list of mounts that first awk line spits out and then 2) call findmnt on that list. That way you're not spawning subshells upon subshells and 5 layers deep for every mount.

Getting not found while using sed command in korn shell

#!/usr/bin/ksh
PATH=$PATH:/usr/bin/sed
export PATH
#echo -n "Enter file name : "
read name
if [ -e $name ]
then
var1="$(wc -l < $name)"
print "Total lines in file including header and trailer : $var1"
var2=$(tail -1 $name|cut -c11-20)
print $var2
var3="$((10#${var2}))"
print "Total record count as per Trailer record: $var3"
val=$(($var1-2))
print "total records w/o header and trailer is : $val"
if [[ $val -eq $var3 ]];then
echo " "
echo "Trailer record information for total record count is true"
else
echo "Trailer record count is erroneous"
fi
#echo -n "Please provide journal date string : "
#read journal
#journal=$(tail -n2 $name|head -n1|cut -c42-45)
#print "grep -c \"$journal\" $name"
journal1=$(tail -n2 $name|head -n1|cut -c42-45)
print $journal1
journal=$("/bin/sed '1d;\$d' $name|cut -c42-45|grep -c \"$journal1\"")
print "sed '1d;\$d' $name|cut -c42-45|grep -c \"$journal1\""
print $journal
if [[ $journal -eq $var3 ]];then
echo "All journals are valid"
else
echo "Please check journals manually."
fi
else
echo "File does not exists"
fi
when i execut ethe above script i get sed command not found error. Same sed command is running on command prompt. Can someone help me out? I have tried using PATH where sed command is found.
when i execute "whereis" for sed. i get this : sed: /usr/bin/sed
Try with little steps, like
echo "Almost solved this"| sed 's/Almost/We have/'
I think the sed in this line will be found with your normal PATH.
Look at the strange sedline in the script.
journal=$("sed '1d;\$d' $name|cut -c42-45|grep -c \"$journal1\"")
will try to assign the stdout of the command to the variable journal. And the command is
"sed '1d;\$d' $name|cut -c42-45|grep -c \"$journal1\""
This is not sed with options piped to other commands, but a very long filename sed '1d;$d' sample.dat|cut -c42-45|grep -c "2016".
This is a completely different from
sed '1d;\$d' $name|cut -c42-45|grep -c \"$journal1\"
or better
sed '1d;$d' "$name" |cut -c42-45|grep -c "$journal1"
When the header and tail line are small, you can avoid sed and $journal1 with
if [ $(cut -c42-45 "$name"| sort -u| wc -l) -eq 1 ]; then
echo "All records have the same journal"
else
echo "Something funny..."
fi
And please indent your file next time.

Grep inside bash script not finding item

I have a script which is checking a key in one file against a key in another to see if it exists in both. However in the script the grep never returns anything has been found but on the command line it does.
#!/bin/bash
# First arg is the csv file of repo keys separated by line and in
# this manner 'customername,REPOKEY'
# Second arg is the log file to search through
log_file=$2
csv_file=$1
while read line;
do
customer=`echo "$line" | cut -d ',' -f 1`
repo_key=`echo "$line" | cut -d ',' -f 2`
if [ `grep "$repo_key" $log_file` ]; then
echo "1"
else
echo "0"
fi
done < $csv_file
The CSV file is formatted as follows:
customername,REPOKEY
and the log file is as follows:
REPOKEY
REPOKEY
REPOKEY
etc
I call the script by doing ./script csvfile.csv logfile.txt
Rather then checking output of grep command use grep -q to check its return status:
if grep -q "$repo_key" "$log_file"; then
echo "1"
else
echo "0"
fi
Also your script can be simplified to:
log_file=$2
csv_file=$1
while IFS=, read -r customer repo_key; do
if grep -q "$repo_key" "$log_file"; then
echo "1"
else
echo "0"
fi
done < "$csv_file"
use the exit status of the grep command to print 1 or 0
repo_key=`echo "$line" | cut -d ',' -f 2`
grep -q "$repo_key" $log_file
if [ $? -eq 1 ]; then
echo "1"
else
echo "0"
fi
-q supresses the output so that no output is printed
$? is the exit status of grep command 1 on successfull match and 0 on unsuccessfull
you can have a much simpler version as
grep -q "$repo_key" $log_file
echo $?
which will produce the same output

I am asking for the user to input a title, but I want to compare the "input" with my text file

#!/bin/bash
library=mylibrary
tmp=mylibrary.tmp
function add_book {
echo Please enter the book title:
read title
existed=`awk -F '$1 == $title' $library | wc -l`
echo $existed
if (( $existed == 0 ))
then
echo Please enter the author:
read author
location=library
updated=$title,$author,$location,`date +%F`
echo $updated >> $library
echo $updated return successfully!
else
echo "Book Exist!"
fi
((WHAT I WANT TO DO IS COMPARE THE TITLES AND IF THE TITLE EXIST THEN ECHO "BOOK EXIST"
I WANT IT TO COMPARE THE INPUT WITH THE TEXT FILE))
HERE IS A SAMPLE TEXT FILE:
Title,Author,Location,Date Added
jj,jj,library,2013-11-14
hjj,hj,library,2013-11-14
jhj,hjh,library,2013-11-14
Your script has quite a few issues but this seems to be a blocker issue:
existed=`awk -F '$1 == $title' $library | wc -l`
Change that to:
existed=$(awk -v title="$title" -F '$1 == title{i++} End{print i}' "$library")
SHELL doesn't expand varialle inside single quotes
To pass a shell variable to awk use -v name="$value" syntax

bash script and greping with command line

new to bash scripting so just wondering if i am doing this code right at all. im trying to search /etc/passwd and then grep and print users.
usage ()
{
echo "usage: ./file.sk user"
}
# test if we have two arguments on the command line
if [ $# != 1 ]
then
usage
exit
fi
if [[ $# < 0 ]];then
usage
exit
fi
# Search for user
fullname=`grep $1 /etc/passwd | cut -f 5 -d :`
firstname=`grep $1 /etc/passwd | cut -f 5 -d : | cut -f 1 -d " "`
#check if there. if name is founf: print msg and line entry
not sure as how to this or if im doing this right...
am i doing this right?
grep $1 /etc/passwd | while IFS=: read -r username passwd uid gid info home shell
do
echo $username: $info
done
This might work for you:
fullname=$(awk -F: '/'$1'/{print $5}' /etc/passwd)
firstname=${fullname/ *}
You're on the right track.
But I think the 2nd if [[ $# < 0 ]] .... fi block doesn't get you much. Your first test case gets the situation right, 'This script requires 1 argument or quits'.
Also, I don't see what you need firstname for, so a basic test is
case "${fullname:--1}" in
-[1] ) printf "No userID found for input=$1\n" ; exit 1 ;;
* )
# assume it is OK
# do what every you want after this case block
;;
esac
You can of course, duplicate this using "${firstname}" if you really need the check.
OR as an equivalent if ... fi is
if [[ "${fullname}" == "" ]] ; then
printf "No userID found for input=$1\n" ; exit 1
fi
note to be more efficient, you can parse ${fullname} to get firstname without all the calls to grep etc, i.e.
firstname=${fullname%% *}
Let me know if you need for me to explain :--1} and %% *} variable modifiers.
I hope this helps.
Instead of this:
fullname=`grep $1 /etc/passwd | cut -f 5 -d :`
firstname=`grep $1 /etc/passwd | cut -f 5 -d : | cut -f 1 -d " "`
Try this:
fullname=$(cut -f5 -d: /etc/passwd | grep "$1")
if [[ $? -ne 0 ]]; then
# not found, do something
fi
firstname=${fullname%% *} # remove the space and everything after
Note that I changed my answer to cut before grep so that it doesn't get false positives if some other field matches the full name you are searching for.
You can simply by reading your input to an array and then printing out your desired fields, something like this -
grep $1 /etc/passwd | while IFS=: read -a arry; do
echo ${arry[0]}:${arry[4]};
done
Test:
jaypal:~/Temp] echo "root:*:0:0:System Administrator:/var/root:/bin/sh" |
while IFS=: read -a arry; do
echo ${arry[0]}:${arry[4]};
done
root:System Administrator

Resources