2

1st Question on stackoverflow, please tell me if I did something wrong.

I want to collect members of the admingroup and compare them to the local users (some accounts excluded via grep -v). Comparing them kinda does not work as its always showing 2 identical strings as not equal.

I'm new to scipting and and heavily rely on googling to get anything done. I tried basically everything I found on stackoverflow via google to no avail and reviewed the similar questions, so while this seems to be a recurring topic I don't think this is a duplicate?

So heres the script:

#!/bin/bash

localuser=$(ls /Users/ | grep -v "Shared\|admin\|.localized\|_appstore\|root")
localadmin=$(dseditgroup -o read admin | grep $localuser)

if [[ "$localadmin" == "$localuser" ]]; then 
echo "true"
else
echo "false"
fi

If I echo both variables using od -c I get the following output:

0000000    m   f   r   a   n   k  \n                                    
0000007
0000000    m   f   r   a   n   k  \n                                    
0000007

Using set -x (leaving out the -if/else) I get the following:

++ ls /Users/
++ grep -v 'Shared\|admin\|.localized\|_appstore\|root'
+ localuser=mfrank
++ dseditgroup -o read admin
++ grep mfrank
+ localadmin='          mfrank'
+ echo mfrank
mfrank
+ echo mfrank
mfrank

Here I can clearly see that $localadmin has two ' and blank spaces. Where do these come from and how can I get rid of them? Why are these not showing with od -c?

Thank you very much for helping.

EDIT:

Correctly using OD shows the following:

localadmin
0000000    004411  063155  060562  065556 0000010                                
localuser
0000000    063155  060562  065556  0000006                                  
tadman
  • 208,517
  • 23
  • 234
  • 262
  • _How_ exactly are you "echoing them with od -c"? Show the exact code you used to feed their content to od. `echo $variable | od` corrupts it; you'd need to use `printf %s "$variable" | od`. (See [I just assigned a variable, but `echo $variable` shows something else!](https://stackoverflow.com/questions/29378566/i-just-assigned-a-variable-but-echo-variable-shows-something-else)) – Charles Duffy Aug 21 '23 at 14:42
  • Also relevant is [Why you shouldn't parse the output of `ls`](https://mywiki.wooledge.org/ParsingLs). Better practice, after `shopt -s extglob`, is `localusers=( /Users/!(Shared|admin|.localized|_appstore|root) ); localusers=( "${localusers[@]#/Users/}" )`. – Charles Duffy Aug 21 '23 at 14:44
  • Anyhow, once you _correctly_ feed the variable to `od` you'll know what the characters are; that's a prerequisite to correctly getting rid of them. Might be something like `localadmin=${localadmin//$'\t'/}`, for example, if the character turns out to be a single tab. – Charles Duffy Aug 21 '23 at 14:45
  • Tyvm for the fast reply. I indeed pipe'ed it. Using printf (which I tried before but couldn't get the syntax right) also showed that they are different. Thank you for linking the resource regarding parsing the output of ls, I'll be taking a look at that. – meatballbeam Aug 21 '23 at 14:47
  • 3
    It's not the pipe that's the problem, it's the unquoted expansion passed as an argument to `echo` (`echo` [can be a problem itself](https://unix.stackexchange.com/questions/65803/why-is-printf-better-than-echo), but it's less of one relative to the unquoted expansion). – Charles Duffy Aug 21 '23 at 14:49
  • BTW, I'd probably retrieve the info you're looking for from `dscacheutil -q group -a name admin`. – Charles Duffy Aug 21 '23 at 14:51
  • don't just describe what you did, **show** us what you did - copy/paste the commands you ran from your desktop into your question. – Ed Morton Aug 21 '23 at 14:51
  • 1
    Also make sure to copy/paste your code into http://shellcheck.net and fix whatever issues that tells you about. – Ed Morton Aug 21 '23 at 14:54
  • @EdMorton, hey, you're the best person for this question; if we have a line that looks like `users: root foo bar baz`, what's the best way to tell awk to print columns from $2 onward that don't match "root" each on their own line? (Trying to figure out what to put after `dscacheutil -q group -a name admin | awk '$1 == "users:"'`) – Charles Duffy Aug 21 '23 at 14:54
  • @CharlesDuffy I'd just use a loop `for (i=2; i<=NF; i++) if ($i != "root") print $i` – Ed Morton Aug 21 '23 at 14:56
  • 1
    ...indeed, that works nicely! – Charles Duffy Aug 21 '23 at 14:57
  • You guys are crazy, thank you for helping out. – meatballbeam Aug 21 '23 at 14:59
  • 1
    @EdMorton I'll be doing that for future questions, thank you. – meatballbeam Aug 21 '23 at 14:59
  • They are not the same. The trace shows `localadmin=' mfrank'`, i.e. you have leading spaces, while `localuser` does not have whitespace. – user1934428 Aug 22 '23 at 06:12

2 Answers2

3

Your immediate problem is because $localadmin is subject to word-splitting and glob expansion, so when you ran echo $localadmin | od the whitespace was removed. Always use "$localadmin" with the quotes to prevent your data from being munged.

When you intentionally want to remove whitespace from a variable, you can do that with ${localadmin//[[:space:]]/}.


For your real problem, finding the name of non-root local admin users on MacOS:

dscacheutil -q group -a name admin |
  awk '$1 == "users:" {
    for (i=2; i<=NF; i++) {
      if ($i != "root") {
        print $i
      }
    }
  }'

Thank you to Ed Morton for helping with the awk. :)

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
0

verbose awk solution without the looping :

 echo '
name: admin
password: *
gid: 80
users: root user1 root root root user2 user3 root' | 

mawk 'gsub("^[^\n]+|\nroot\5", _, 
           $!(NF *= $1 == "users:")) + gsub(/^\n+|\5+|\n+$/, "")' OFS='\5\n'

user1
user2
user3
RARE Kpop Manifesto
  • 2,453
  • 3
  • 11