How to find list of groups that are present in '/etc/passwd' but not in the '/etc/group'? - bash

I want to find list of groups that are present in /etc/passwd but not in the /etc/group
I have written the following command so far
for user in $(getent passwd | cut -d: -f1); do
printf "%s: %s\n" $user "$(id -nG $user)"
done

$ id -Gz | cat -v
197121^#114^#197610^#544^#545^#4^#66049^#11^#15^#113^#4095^#66048^#262154^#405504^#$
$ getent passwd | tr '[:alpha:]' '#'
########:*:197609:197121:#-#######53\########,#-1-5-21-2486228713-2700429697-662227502-1001:/####/########:/###/####
##############:*:544:544:#-#######\##############,#-1-5-32-544:/:/####/#######
######:*:18:18:#-## #########\######,#-1-5-18:/####/######:/###/####
##### #######:*:19:19:#-## #########\##### #######,#-1-5-19:/:/####/#######
####### #######:*:20:20:#-## #########\####### #######,#-1-5-20:/:/####/#######
##############:*:544:544:#-#######\##############,#-1-5-32-544:/:/####/#######
## #######+################:*:328384:328384:#-## #######\################,#-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464:/:/####/#######
$ awk 'NR==FNR{grps[$0];next} !($3 in grps){print $3}' RS='\0' <(id -Gz) RS='\n' FS=':' <(getent passwd)
197609
18
19
20
328384

Could you please try following, where it will give entries of those group names which are present in /etc/passwd and NOT in /etc/group.
awk -F':' 'FNR==NR{a[$5]=$0;next} ($1 in a){delete a[$1]} END{for(i in a){print a[i]}}' /etc/passwd /etc/group

with grep/cut
$ grep -vxFf <(cut -d: -f1 </etc/group) <(cut -d: -f1 </etc/passwd)

Related

How to grab fields in inverted commas

I have a text file which contains the following lines:
"user","password_last_changed","expires_in"
"jeffrey","2021-09-21 12:54:26","90 days"
"root","2021-09-21 11:06:57","0 days"
How can I grab two fields jeffrey and 90 days from inverted commas and save in a variable.
If awk is an option, you could save an array and then save the elements as individual variables.
$ IFS="\"" read -ra var <<< $(awk -F, '/jeffrey/{ print $1, $NF }' input_file)
$ $ var2="${var[3]}"
$ echo "$var2"
90 days
$ var1="${var[1]}"
$ echo "$var1"
jeffrey
while read -r line; do # read in line by line
name=$(echo $line | awk -F, ' { print $1} ' | sed 's/"//g') # grap first col and strip "
expire=$(echo $line | awk -F, ' { print $3} '| sed 's/"//g') # grap third col and strip "
echo "$name" "$expire" # do your business
done < yourfile.txt
IFS=","
arr=( $(cat txt | head -2 | tail -1 | cut -d, -f 1,3 | tr -d '"') )
echo "${arr[0]}"
echo "${arr[1]}"
The result is into an array, you can access to the elements by index.
May be this below method will help you using
sed and awk command
#!/bin/sh
username=$(sed -n '/jeffrey/p' demo.txt | awk -F',' '{print $1}')
echo "$username"
expires_in=$(sed -n '/jeffrey/p' demo.txt | awk -F',' '{print $3}')
echo "$expires_in"
Output :
jeffrey
90 days
Note :
This above method will work if their is only distinct username
As far i know username are not duplicate

"Merge" paths and display all matchs in one line

I need help, I have that :
/usr/include/slang.h:include
/usr/include/slang.h:star
/usr/include/slang.h:load
/usr/include/byteswap.h:include
and I must merge to have result here in shell:
/usr/include/slang.h include star load
/usr/include/byteswap.h include
How can I do ?
$ cat merge.txt
/usr/include/slang.h:include
/usr/include/slang.h:star
/usr/include/slang.h:load
/usr/include/byteswap.h:include
$ cat merge.sh
#!/bin/bash
for path in $(awk -F':' '{print $1}' merge.txt | uniq)
do
values=$(grep "${path}" merge.txt | awk -F':' '{print $NF}' | tr '\n' ' ')
echo "${path}" "${values}"
done
Output:
$ ./merge.sh
/usr/include/slang.h include star load
/usr/include/byteswap.h include

Running last in awk [Solaris]

How can i run last command for every matched line in awk?
ypcat passwd | awk -F":" '/:John / { system("last" $1) }'
I'm trying to execute the last command for every user that is named John but It does not print anything.
Insert a whitespace after last:
ypcat passwd | awk -F":" '/:John / {system("last " $1)}'
Your approach is wrong, awk is not shell. awk is designed to manipulate text not to call other tools from, that is what a shell is for This may be what you want, depending on what last is/does:
ypcat passwd | awk -F":" '/:John /{print $1}' | xargs last
or:
ypcat passwd | awk -F":" '/:John /{print $1}' | xargs -n 1 last

loop passwd in shell script

how can i make a loop for users of server using shell script
i wrote this code ..
#!/bin/bash
b=`awk -F: '{ print $1 }' /etc/passwd | sort`
for $b in /home/$b/ /home/$b/ /home/$b/
echo "$b"
done
i want to loop all users and show its
the users like in file
/etc/passwd
like :
root,admin,cpanel,adm,mysql,user1,user2,user3,user4,user5
i want the output :
/home/adm
/home/root
/home/admin
/home/mysql
/home/user1
/home/user2
and thanks
To print /home/ before each user name:
$ awk -F: ' { print "/home/"$1 }' /etc/passwd | sort
/home/avahi
/home/backup
/home/bin
/home/clamav
/home/colord
/home/daemon
[...snip...]
If you want actual home directories, you can find them in field 6:
$ awk -F: ' { print $6 }' /etc/passwd | sort
/dev/null
/home/john1024
/nonexistent
/root
You can also access the sixth field using cut:
cut -d: -f6 /etc/passwd | sort
Printing multiple directories
As per the revised question in the comments:
$ awk -F: ' { p="/home/"$1; printf "%s\n%s\n%s\n",p"/www",p"/ftp",p"/etc" }' /etc/passwd | sort
/home/avahi/etc
/home/avahi/ftp
/home/avahi/www
/home/backup/etc
/home/backup/ftp
/home/backup/www
[...snip...]

Print out onto same line with ":" separating variables

I have the following piece of code and would like to display HOST and RESULT side by side with a : separating them.
HOST=`grep pers results.txt | cut -d':' -f2 | awk '{print $1}'`
RESULT=`grep cleanup results.txt | cut -d':' -f2 | awk '{print $1}' | sed -e 's/K/000/' -'s/M/000000/'`
echo ${HOST}${RESULT}
Please can anyone assist with the final command to display these, I am just getting all of hosts and then all of results.
You probably want this:
HOST=( `grep pers results.txt | cut -d':' -f2 | awk '{ print $1 }'` ) #keep the output of the command in an array
RESULT=( `grep cleanup results.txt | cut -d':' -f2 | awk '{ print $1 }' | sed -e 's/K/000/' -'s/M/000000/'` )
for i in "${!HOST[#]}"; do
echo "${HOST[$i]}:${RESULT[$i]}"
done
A version that works without arrays, using an extra file handle to read from 2 sources at at time.
while read host; read result <&3; do
echo "$host:$result"
done < <( grep peers results.txt | cut -d: -f2 | awk '{print $1}' ) \
3< <( grep cleanup results.txt | cut -d':' -f2 | awk '{print $1}' | sed -e 's/K/000/' -'s/M/000000/')
It's still not quite POSIX, as it requires process substitution. You could instead use explicit fifes. (Also, an attempt to shorten the pipelines that produce the hosts and results. It's probably possible to combine this into a single awk command, since you can either do the substitution in awk, or pipe to sed from within awk. But this is all off-topic, so I leave it as an exercise to the reader.)
mkfifo hostsrc
mkfifo resultsrc
awk -F: '/peers/ {split($2, a, ' '); print a[1]}' results.txt > hostsrc &
awk -F: '/cleanup/ {split($2, a, ' '); print a[1]}' results.txt | sed -e 's/K/000' -e 's/M/000000/' > resultsrc &
while read host; read result <&3; do
echo "$host:$result"
done < hostsrc 3< resultsrc

Resources