Home > Back-end >  Multiple conditions in a shell script for loop
Multiple conditions in a shell script for loop

Time:11-12

I'm trying to loop through 2 groups on macOS and remove users in the admin group if they don't exist in another group.

newadmins=$(dscl . -read Groups/newadmin GroupMembership | cut -c 18-)
adminUsers=$(dscl . -read Groups/admin GroupMembership | cut -c 18-)

for (user in $adminUsers && ! user in $newadmins)
do
        dseditgroup -o edit -d $user -t user admin
        if [ $? = 0 ]; then echo "Removed user $user from admin group"; fi
    else
        echo "Admin user $user left alone"
    fi
done

The above didn't work. I think I'm confusing shell with other languages. Any help would be appreciated. Thank!

The below script worked exactly as expected:

NEW_ADMIN_USERS=$(dscl . -read Groups/newadmin GroupMembership | cut -d ' ' -f 2-)
ADMIN_USERS=$(dscl . -read Groups/admin GroupMembership | cut -d ' ' -f 2-)

DEFUNCT_ADMIN_USERS=$(grep -vxFf <(echo ${NEW_ADMIN_USERS} | tr ' ' '\n') <(echo ${ADMIN_USERS} | tr ' ' '\n'))

for DEFUNCT_ADMIN_USER in ${DEFUNCT_ADMIN_USERS}
do
    if dseditgroup -o edit -d ${defunct_admin_user} -t user admin
  then
    echo "Removed user ${DEFUNCT_ADMIN_USER} from admin group"
  else
    echo "Admin user ${DEFUNCT_ADMIN_USER} left alone"
  fi  
done

Thanks @msbit for all the help!

CodePudding user response:

I would consider doing something like this:

#!/usr/bin/env bash

set -eu

NEW_ADMIN_USERS=$(dscl . -read Groups/newadmin GroupMembership | cut -d ' ' -f 2-)
ADMIN_USERS=$(dscl . -read Groups/admin GroupMembership | cut -d ' ' -f 2-)

DEFUNCT_ADMIN_USERS=$(grep -vxFf <(echo ${NEW_ADMIN_USERS} | tr ' ' '\n') <(echo ${ADMIN_USERS} | tr ' ' '\n'))

for DEFUNCT_ADMIN_USER in ${DEFUNCT_ADMIN_USERS}
do
  if dseditgroup -o edit -d ${DEFUNCT_ADMIN_USER} -t user admin
  then
    echo "Removed user ${DEFUNCT_ADMIN_USER} from admin group"
  else
    echo "Admin user ${DEFUNCT_ADMIN_USER} left alone"
  fi  
done

The main thrust of this is using the grep command put forward by @Jetchisel with process substitution (<()) to prepare a list of admin users in the ADMIN_USERS variable but not in the NEW_ADMIN_USERS variable, then iterating over that variable.

This departs from your approach in a number of ways:

  • setting the errexit and nounset options which will cause the script to exit on any error code from a command, including use of unset variables (set -eu)
  • using the field argument of cut with delimiter set to space when parsing the output of dscl (cut -d ' ' -f 2-)
  • subsequently splitting the list of users into lines with tr (tr ' ' '\n')
  • passing the list through to for as appropriate (using ( was a syntax error, as I suspect the use of ! would be)
  • evaluating the return code of dseditgroup directly as that is what if is testing for
  • removing the trailing fi for the first if command, as it's not needed when you have the else (and would cause a syntax error due to an apparent floating else)

Please test thoroughly, preferably with a dummy command instead of dseditgroup before you're 100% happy that this works as expected, and consider setting the xtrace option (set -x which will echo all the commands as they are executed), while developing.

  • Related