Print all users from one, selected group (GID) from /etc/passwd - bash

I'm new here and I'm trying to learn some Bash. I need to write a script that will print all of users from etc/passwd in seperated lines from one, selected group defined as a $1 parameter.
When group does not exist, the proper message is displayed. Same for wrongly entered numbers. I manage to do this, which do all the job with messages, but I just can't find the way to write users from one group.
Example:
myscript.sh 1000
Output:
User1
User2
User3
Here's the bash file:
#!/bin/bash
awk -F':' '{ print $3 }' /etc/passwd | sort > list.txt
re='^[0-9]+$'
if grep -Fxq "$1" list.txt
then
**echo "don't know what to do here"**
else
if ! [[ $1 =~ $re ]] ; then
echo "Wrong number"
else
echo "Group does not exist"
fi
fi

Related

How to Assign User-Account Name and Directory to Array in Bash/sh?

I am attempting to create a bash script for the STIG test with the vulnerability ID V-72017 on a Red Hat Enterprise Linux (RHEL) system. I am tasked with making sure all user permissions have the octal value of 0750 or less
I have the ability to gather the permission octal value a user by using
stat -c "%a" /home/$username
I am trying to create a $username (or directory) array by utilizing the command (outputs name of each user on the system):
eval getent passwd {$(awk '/^UID_MIN/ {print $2}' /etc/login.defs)..$(awk '/^UID_MAX/ {print $2}' /etc/login.defs)} | cut -d: -f1
I plan to map this output to an array, possibly a while loop. Is this a possible solution?
Syntax Error from the following:
(eval getent passwd {$(awk '/^UID_MIN/ {print $2}' /etc/login.defs)..$(awk '/^UID_MAX/ {print $2}' /etc/login.defs)} | cut -d: -f1) | while read -r line
do
myarray+=line
stat -c "%a" /home/$line
done
Desired Output Case 1:
Users:
rob
bob
Exit Fail: bob has permission octal value 0755.
Desired Output Case 2:
Users:
rob
bob
Exit Pass: All users have permission octal value of 0750 or less.
You have found all login users. Regexp can be used to check home dir's permissions.
echo "Users: "
(eval getent passwd {$(awk '/^UID_MIN/ {print $2}' /etc/login.defs)..$(awk '/^UID_MAX/ {print $2}' /etc/login.defs)} | cut -d: -f1) | while read -r line
do
echo $line
perm=$(stat -c "%a" /home/$line)
[[ "$perm" =~ [0-7][0,1,4,5][0] ]] || echo "Exit fail: $line has permission octal value $perm"
done
Maybe you want to adjust the output form.
It is suggested to avoid using eval as much as possible. All the more
if you are investigating the system security status. Please try the
following instead:
#!/bin/bash
perm=0750 # system policy
uid_min=$(sed -n '/^UID_MIN/ s/[^0-9]*\([0-9]\+\).*/\1/p' "/etc/login.defs")
uid_max=$(sed -n '/^UID_MAX/ s/[^0-9]*\([0-9]\+\).*/\1/p' "/etc/login.defs")
# read /etc/passwd and process line by line
while IFS=: read -ra a; do
# now ${a[0]} holds username and ${a[2]} holds uid
if (( ${a[2]} >= uid_min && ${a[2]} <= uid_max )); then
# narrow down the users whose uid is within the range
users+=("${a[0]}")
# check the user's permission
userperm="0$(stat -c "%a" "/home/${a[0]}")"
if (( (~ perm) & userperm )); then
# the user's permission exceeds the limitation $perm
fail+=("$(printf "%s has permission octal value 0%o." "${a[0]}" "$userperm")")
fi
fi
done < "/etc/passwd"
echo "Users:"
for i in "${users[#]}"; do
echo "$i"
done
if (( ${#fail[#]} == 0 )); then
printf "Exit Pass: All users have permission octal value of 0%o or less.\n" "$perm"
else
for i in "${fail[#]}"; do
printf "Exit Fail: %s\n" "$i"
done
fi
Hope this helps.

How to add one loop arrays value to another loop

I have a script which finds all the Cert. identity on the system then I want to add one more Cert. and delete rest of Cert except which is added recently.
1st loop finds how many identity we have in the system and set array as $var1 $var2 $var3 and no. continues.
Then I add one more.
Now I want to delete 1 loop array values herein for loop. But my $DEL variable is coming as var1 var2, not the actual identity we have set in 1st loop.
#!/bin/bash
DOM=$(/usr/sbin/dsconfigad -show | grep "Active Directory Domain" | awk '{ print $5 }')
MAC=$(/usr/sbin/dsconfigad -show | grep "Computer Account" | awk '{ print $4 }' | tr -d "$")
HOST=$MAC.$DOM
CRT=$(security find-identity -v | grep $HOST | awk '{ print $2}')
set - $CRT; index=0; while [ "$1" ];
do
let index=$index+1;
eval var${index}="$1";
shift;
done
The above loop result is: + set - 02KFKDSF89SFMDFMFS7908934M90DODFSMN78345 K69SKLD04KCM62469933FA60567LLFD730957FA3 489FHDFS93MDF89UY2345905DFSKDDSKFDS9FSEF
echo $var1
echo $var2
The echo $var1 is: 02KFKDSF89SFMDFMFS7908934M90DODFSMN78345
The echo $var2 is: K69SKLD04KCM62469933FA60567LLFD730957FA3
NUMCRT=$index
echo "Number of Certificate is $NUMCRT"
echo "Add of Script part bla bla Start"
echo "ADD one more Cert"
echo "Add of Script part bla bla END"
Now I want to delete array value which I have found in the 1st loop. But $DEL value is coming var1 var2 var3 and not 02KFKDSF89SFMDFMFS7908934M90DODFSMN78345 K69SKLD04KCM62469933FA60567LLFD730957FA3 489FHDFS93MDF89UY2345905DFSKDDSKFDS9FSEF.
for i in $(seq 1 $NUMCRT);
do
let $i;
DEL="var$i"
echo "Delete number $DEL"
done
It should show echo "Delete number $DEL" in 2nd for loop value as below.
Delete number 02KFKDSF89SFMDFMFS7908934M90DODFSMN78345
Delete number K69SKLD04KCM62469933FA60567LLFD730957FA3
Delete number 489FHDFS93MDF89UY2345905DFSKDDSKFDS9FSEF
You don't need to run dsconfigad twice.
domain=''; machine='';
while IFS="=" read key val
do case "$key" in
"Active Directory Domain"*) domain=$( echo $val ) ;; # strip space
"Computer Account"*) machine=$( echo $val ) ;; # strip space
*) if [[ -n "$domain" ]] &&
[[ -n "$machine" ]]
then break
fi;;
esac
done < <( /usr/sbin/dsconfigad -show )
I can't find a good example of security find-identity -v including the verbose option, so I'm going to fake my way through this by using your command structure, though I'm still not going to use all-caps vars (don't do that).
declare -a crt=( $( security find-identity -v | grep $machine.$domain | awk '{ print $2}' ) )
Now the values are in the $crt[#] array, numerically indexed.
If you know the index of the one you want to remove,
unset "crt[$num]"
This does leave a hole in your array...
Alternately, use the values themselves as the keys, and you won't have to track the indexes and leave holes in your array.
declare -A crt=()
while read one
do crt[$one]=1
done < <( security find-identity -v | grep $HOST | awk '{ print $2}' ) )
Now you can remove it with
unset "crt[$val]" # assuming $val is the key
or walk through them with
for val in "${!crt[#]}"
do : something with the key...
done
Useful?
As always, folks please check me. I don't have either of these tools installed. Working blind and likely screwing something up. :)

Check if read input is available in var

I try to list all Folders in /etc/nginx/html/.
I use this code:
folders=`for i in $(ls -d */ | grep -Ev "(backups)"); do echo ${i%%/}; done`
Now I get all folders with names non containing "backups".
Now I want to count the folder and save it into var. I use this code:
folders_count=( $folders )
echo ${#folders_count[#]}
It works. I get a list of all folders and I can count the folders.
Now I try to write a script that reads user input (or better, offers 1,2,3 choices, but I do not know how to do it) and install a script like Prestashop (as an example) into the folder the user has chosen if there are more than 1 folder.
Here is my code:
folders_count=( $folders )
echo ${#folders_count[#]}
echo
if [ ${#folders_count[#]} -gt 1 ]; then
echo "${info} Please choose a Website for Prestashop:" | awk '{ print strftime("[%H:%M:%S] |"), $0 }'
# for websites in $folders; do
# echo "${info} $folders" | awk '{ print strftime("[%H:%M:%S] |"), $0 }'
# echo "${info} $folders" | awk '{ print strftime("[%H:%M:%S] |"), $0 }'
# done
echo "$folders" | awk '{ print strftime("[%H:%M:%S] |"), $0 }'
#read websites_for_prestashop
read -e -i "$websites_for_prestashop" -p "Please enter your Website: " input
websites_for_prestashop="${input:-$websites_for_prestashop}"
if [ "$websites_for_prestashop" == "$folders" ]; then
echo "Website is $websites_for_prestashop"
else
echo "Website $websites_for_prestashop is not found"
fi
#echo "${info} Please choose a Website for Prestashop:" | awk '{ print strftime("[%H:%M:%S] |"), $0 }'
else
echo "nix"
fi
I dont know how I can make it work :/
Can anyone help me to fix it?
I would like to list the available folders. Then the user should be able to select in which folder he would like to install the script.
Folders:
[0] Folder 1
[1] Folder 2
[2] Folder 3
[3] Folder 4
....
Then a query will appear:
Please select an order for the installation:
The selection of the folder should then be stored in the variable read websites_for_prestashop
I do not know what your script is supposed to be doing. There are a bunch of lines commented out, the only assignment for websites_for_prestashop contains a reference to the variable itself and occurs after this variable is first used, and the script ultimately does nothing.
What I can tell you is that the way you extract filenames is not the right way to do it. Parsing the output of ls is not a good idea (see http://mywiki.wooledge.org/ParsingLs), and the way you do it will fail on any file with a name containing spaces, among other things.
This would be one way to do it:
declare -a folders=()
for f in */
do
[[ $f =~ backups ]] || folders+=("${f%/}")
done
echo "Folder count is: ${#folders[#]}"
Thank you for your help#
https://stackoverflow.com/users/2416376/olli-k
https://stackoverflow.com/users/7422249/fred
Solution is easy:
folders=`for i in $(ls -d */ | grep -Ev "(backups)"); do echo ${i%%/}; done`
echo "${info} Please choose a Website for Prestashop:" | awk '{ print strftime("[%H:%M:%S] |"), $0 }'
select websites_for_prestashop in $folders;
do
echo "You picked $websites_for_prestashop."
break
done

Why does my bash script hang?

I'm working on a bash script that will check +1000 domains if they are expired. I use a a for loop to iterate over all users in /var/cpanel/users/*. It works great for like the 10 first users (loops) then it just hangs.
A weird thing is that I can stop the script with Ctrl+Z and then start the script again with fg and it continues to work normal for about +10 users but then it hangs again.
This is my scirpt:
# File that will have the result.
file="domain-result.txt"
printf "USER\t\tDOMAIN\t\t\tREPORT\n" > "$file"
printf "\n" >> "$file"
# For loop to iterate over all users in cpanel.
for z in /var/cpanel/users/*;
do
# Only files can be used.
if [[ -f "$z" ]]
then
# Get the domain name.
awk -F'=' '/DNS=/ {print $2}' "$z" | while read row;
do
# If there's no domain name than skip to next account.
if [[ -z "$row" ]]; then continue; fi
printf "Checking domain: %s...done\n" "$row"
# Execute whois command on the domain.
whois=$( /usr/bin/whois $row | grep 'not found' )
# Get the username.
user=$( echo "$z" | awk -F'/' '{print $5}' )
if [[ -n "$whois" ]]
then
printf "%s\t\t%s\t\t%s - EXPIRED\n" "$user" "$row" "$whois" >> "$file"
break
else
continue
fi
done
else
continue
fi
done
printf "\n"
printf "Total: $( sed '1,2d' "$file" | wc -l ) expired domains.\n"
This is a sample of how the files in /var/cpanel/users/* look like:
DNS=stackoverflow.com
Thank you Ignacio Vazquez-Abrams for pointing out WHOIS abuse. I got it to work by adding a sleep 2 to the for loop. Now it works great.

Check if a particular string is in a file bash

I want to write a script to check for duplicates
For example: I have a text file with information in the format of /etc/passwd
alice:x:1008:555:William Williams:/home/bill:/bin/bash
bob:x:1018:588:Bobs Boos:/home/bob:/bin/bash
bob:x:1019:528:Robt Ross:/home/bob:/bin/bash
james:x:1012:518:Tilly James:/home/bob:/bin/bash
I want to simply check if there are duplicate users and if there are, output the line to standard error. So in the example above since bob appears twice my output would simply generate something like:
Error duplicate user
bob:x:1018:588:Bobs Boos:/home/bob:/bin/bash
bob:x:1019:528:Robt Ross:/home/bob:/bin/bash
Right now I have a while loop that reads each line and stores each piece of information in a variable using awk -F that is delimited with ":". After storing my username I am not too sure on the best approach to check to see if it already exists.
Some parts of my code:
while read line; do
echo $line
user=`echo $line | awk -F : '{print $1}'`
match=`grep $user $1`($1 is the txtfile)
if [ $? -ne 0 ]; then
echo "Unique user"
else
echo "Not unique user"
then somehow grep those lines and output it
fi
done
The matching does not produce the right results
Suggestions?
instead of re-inventing the wheel, use the following tools:
cut to extract first field
sort and uniq to keep duplicated lines only.
cut -d : -f 1 | sort | uniq -d | while read i ; do
echo "error: duplicate user $i"
done
Sounds like a job for awk to me:
% awk -F':' '
/:/ {
count[$1] += 1
}
END {
for (user in count) {
if (count[user] > 1) {
print user " appears in the file " count[user] " times."
}
}
}
' /etc/passwd
A perl-proposal:
perl -F: -lanE 'push #{$h{$F[0]}},$_; END{for $k (keys %h){if(#{$h{$k}}>1){say "Error";say for #{$h{$k}}}}}' file

Resources