I have file(sudoers) in which lines can be identical except for the first word in them (different users can execute the same set of commands). I am able to extract such a line using the command:

# grep -v '^ *%' /etc/sudoers |egrep "$users_in_which_I_am_interested | sort|awk '{sub(/^[ \t]+/, "")};1'

tom ALL = NOPASSWD:/usr/bin/pwdadm,/usr/bin/chsec,/usr/bin/chuser   
jim ALL = NOPASSWD:/usr/bin/pwdadm,/usr/bin/chsec,/usr/bin/chuser   
mark ALL = NOPASSWD:/usr/bin/pwdadm,/usr/bin/chsec,/usr/bin/chuser  
peter ALL = NOPASSWD:/usr/bin/pwdadm,/usr/bin/chsec,/usr/bin/chuser

(/etc/sudoers is before that prepared to remove leading and trailing tabs and spaces and replace all multiple whitespaces between words single space). Those lines are identical except for the user. I want to be able to extract lines which have the same command statements configured with the same permissions but different users, and assign the username to a variable and run it through a loop.

# grep -v '^ *%' /etc/sudoers |egrep "$users_in_which_I_am_interested | sort|awk '{sub(/^[ \t]+/, "")};1'| uniq -cf 1
4 tom ALL = NOPASSWD:/usr/bin/pwdadm,/usr/bin/chsec,/usr/bin/chuser

The problem for me comes here - how to find all the lines in the file which contain

"ALL = NOPASSWD:/usr/bin/pwdadm,/usr/bin/chsec,/usr/bin/chuser"

and assign the user in those lines to a variable? "grep"-ing for the string is not easily possible, it is an unknown string and might contain many characters that have to be escaped. ksh, awk and sed can be used (no perl/python and other scr. languages).

awk '
  {user = $1; $1 = ""; users[$0] = users[$0] sep user; sep = " "}
  END {for (key in users) {print key ">" users[key]}}

Given your sample input, this produces

 ALL = NOPASSWD:/usr/bin/pwdadm,/usr/bin/chsec,/usr/bin/chuser>tom jim mark peter
| improve this answer | |
  • It always amazes me how much you can do with awk – Nifle Jul 17 '11 at 12:27

I know you didn't want a perl solution but here is one anyway. It has the advantage that it can handle if the allowed commands are in different order.

is treated as equal to

use strict;
use warnings;

my %hash;

# reads all lines from STDIN
while(<>) {
    chomp;  # removes leading/trailing whitespace

    #splits the input string on whitespace
    my ($user, undef, undef, $cmds) = split;    

    # pulls out list of allowed commands
    my($NP, $cmdlist) = split(/:/, $cmds);

    # Sorts the list of commands and puts it together again
    $cmds = "$NP:".join(',', sort split(/,/, $cmdlist));

    # if the command exists in the hash with a user
    # add the nest user else just adds user                      
    $hash{$cmds} = $hash{$cmds}? "$hash{$cmds} $user" : $user;

# print all                                          
foreach my $key (keys %hash) {
    print "$key | ", $hash{$key} ,"\n";
| improve this answer | |

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.