0

What I want to do :

  • Takes user & their home directory list from /etc/passwd
  • use the username and home address as parameters and change ownership of those directory to that user.

Current outputs:

trimmed output of: cat /etc/passwd

##
# User Database
# 
# Note that this file is consulted directly only when the system is running
# in single-user mode.  At other times this information is provided by
# Open Directory.
#
# See the opendirectoryd(8) man page for additional information about
# Open Directory.
##
nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false
root:*:0:0:System Administrator:/var/root:/bin/sh
daemon:*:1:1:System Services:/var/root:/usr/bin/false
_uucp:*:4:4:Unix to Unix Copy Protocol:/var/spool/uucp:/usr/sbin/uucico
_taskgated:*:13:13:Task Gate Daemon:/var/empty:/usr/bin/false
_networkd:*:24:24:Network Services:/var/networkd:/usr/bin/false

trimmed output of: cat /etc/passwd | grep "^[#;]" -v

nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false
root:*:0:0:System Administrator:/var/root:/bin/sh
daemon:*:1:1:System Services:/var/root:/usr/bin/false
_uucp:*:4:4:Unix to Unix Copy Protocol:/var/spool/uucp:/usr/sbin/uucico
_taskgated:*:13:13:Task Gate Daemon:/var/empty:/usr/bin/false
_networkd:*:24:24:Network Services:/var/networkd:/usr/bin/false
_installassistant:*:25:25:Install Assistant:/var/empty:/usr/bin/false

Now I had to remove the starting '_' so I used follwing command:

trimmed output of: cat /etc/passwd | grep "^[#;]" -v | sed s/"_"//

nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false
root:*:0:0:System Administrator:/var/root:/bin/sh
daemon:*:1:1:System Services:/var/root:/usr/bin/false
uucp:*:4:4:Unix to Unix Copy Protocol:/var/spool/uucp:/usr/sbin/uucico
taskgated:*:13:13:Task Gate Daemon:/var/empty:/usr/bin/false
networkd:*:24:24:Network Services:/var/networkd:/usr/bin/false

Finally I took out the username and directory using awk command:

cat /etc/passwd | grep "^[#;]" -v | sed s/"_"// | awk -F\: '{print $1" "$6}' (output trimmed)

nobody /var/empty
root /var/root
daemon /var/root
uucp /var/spool/uucp
taskgated /var/empty
networkd /var/networkd
installassistant /var/empty
lp /var/spool/cups
postfix /var/spool/postfix
scsd /var/empty
ces /var/empty
appstore /var/db/appstore

Here's what I want to do further!

Take the FIRST argument and assign the ownership of SECOND argument to it. How should I proceed?

1
  • 1
    wrt grep "^[#;]" -v - it's VERY unusual to provide the grep options after the pattern (regexp or string) you want to grep. In 40+ years of shell programming I don't think I've ever seen that and I had to test it to see if it'd actually work. Writing grep foo -v instead of grep -v foo is like saying print lines that contain foo not rather than print lines that don't contain foo. It just makes the code a bit harder to read and more likely to be misunderstood.
    – Ed Morton
    Jul 16 '20 at 14:38
1

the last file can be processed in bash in this way:

cat YOURFILE | \
while read CMD; do 
   field1=${CMD% *};    # take first field
   field2=${CMD#* };    # take second field
   echo do $field1 with $field2;  # do something with it
done

So you have to replace the echo with the appropriate chown command in order to get it working. Taken from Read line by line in bash script

2
  • 2
    Better: let read assign to the different variables directly: while read -r field1 field2; do ...; done < yourfile Jul 16 '20 at 13:54
  • Quote your variables in the do line to avoid globbing, etc.
    – Ed Morton
    Jul 16 '20 at 14:22
1

I am unsure about your intend to remove leading _ this might lead to chown: _user not found when chowning

try

awk -F: '/^#/ { next } {printf "chown %s \"%s\"\n",$1,$6} ' my_etc_passwd

where

  • -F: tell awk to use : as separator
  • /^#/ { next } skip line starting with #
  • likewise add /^_/ { next } to skip line starting with _ (if need be)
  • next pattern/action print chown command.

If result look OK, just pipe to bash

1
  • Using awk to tell shell what to do (chown) is inversion of control and tightly couples the awk script to the bash script that calls it. Just let awk parse the input and generate output then let the shell decide what to do with that output. What you have right now is kinda like having main() in a C program call a function to do something and having that functions return tell main() the name of the function to call next plus the values to pass to that function instead of it just returning values and letting main() decide what to do with them.
    – Ed Morton
    Jul 16 '20 at 15:07
0

You don't need a bunch of other commands and pipes when you're using awk. The correct way to write:

cat /etc/passwd | grep "^[#;]" -v | sed s/"_"// | awk -F\: '{print $1" "$6}'

is:

awk -F':' '!/^[#;]/{sub(/_/,""); print $1, $6}' /etc/passwd

and to then use that output in the shell is (slightly modified the awk command to produce :-separated output since it has :-separated input and so there must be no :s in the input field values):

while IFS=':' read -r user dir; do
    echo chown -- "$user" "$dir"
done < <(awk 'BEGIN{FS=OFS=":"} !/^[#;]/{sub(/_/,""); print $1, $6}' /etc/passwd)

or if you prefer:

awk 'BEGIN{FS=":"; OFS=ORS} !/^[#;]/{sub(/_/,""); print $1, $6}' /etc/passwd |
xargs -n 2 bash -c 'echo chown "$1" "$2"' bash

Remove the echo when you're done testing and want to actually execute chown

0

One way could also be

cat /etc/passwd | grep "^[#;]" -v | sed s/"_"// | cut -d: -f1,6 | xargs -L -t chown

Wherein I did not rewrite your pipeline, rather appended to it.

-1

Using @Archemar 's answer I used the following code which worked for me! Thanks for pointing out that removing _ will give error.

awk -F\: '/^#/{next}''{system("chown " $1" " $6)}' /etc/passwd

4
  • 1
    Happy to gave you an hint, in your code, there is no need for double single quote between next and system juste use ... next} { system(
    – Archemar
    Jul 16 '20 at 14:58
  • Don't do that. It's buggy and inefficient and in terms of calls you're doing shell { awk { system { shell { chown } } } } instead of just shell { awk; chown }.
    – Ed Morton
    Jul 16 '20 at 15:02
  • @EdMorton do you mean I should use something like ``` awk -F\: '/^#/{next}' /etc/passwd;chown $1 $6 ``` Jul 16 '20 at 15:08
  • I mean you should do exactly what I show in my answer. Let awk parse the input data and output the values you want from it and then let the calling shell decide what to do with those values. So shell calls awk to get the values it needs from the input file and the same shell calls chown to do what it wants with those values. Your code is spawning a separate shell sub-process once per line and using awk as a shell (a tool to sequence calls to other tools) and doing it in a way that's vulnerable to malicious code injection and other issues.
    – Ed Morton
    Jul 16 '20 at 15:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.