10

I have an example /etc/passwd file like this:

tom:x:1000:1000:Work:/home/tom:/bin/bash
george:x:1000:1000:Work:/home/george:/bin/bash
bla:x:1000:1000:Work:/home/bla:/bin/bash
boo:x:1000:1000:Work:/home/boo:/bin/bash
bee:x:1000:1000:Work:/root/list:/bin/bash

I'm trying to list all users with a home folder in /home/.

I wrote

cat ~/Desktop/e.txt |awk -F ":" '{if ($6 ~/^/home/) print $1;}'

where e.txt is the text I copied here.

I understand there is a problem with the backslash which is an escape character, but how do I fix it so I can list them in one line of a command?

10
  • 11
    A general advice besides the answers (that are coming with just one second in between!): It almost never makes sense to cat and pipe to tools like awk or sed or the like, as they can be given the filename as an argument.
    – Philippos
    Nov 7, 2017 at 9:30
  • 1
    cat e.txt| grep home | cut -d ":" -f 1 Nov 7, 2017 at 9:56
  • 3
    As all users have a UID of 1000 things might be a little tricky.
    – StrongBad
    Nov 7, 2017 at 12:21
  • 1
    @ArpitAgarwal grep home e.txt | cut -d ":" -f 1. Congratulations on your UUOC award. However, this is not a good solution. What if there is a service "homebackups" that runs under a separate user named after the daemon? That's a false positive.
    – Maya
    Nov 7, 2017 at 12:52
  • 2
    @Philippos re "It almost never makes sense to cat and pipe to tools [that] can be given the filename as an argument" - I generally use cat > tool variant of such command lines because it can improve clarity by maintaining the left-to-right flow of the input-processing-output of the pipe chain. It is less efficient, but almost always minimally so. It also makes adding a progress bar where relevant & useful when running interactively very obvious (just replace cat with pv if that is installed). Nov 7, 2017 at 16:45

6 Answers 6

12

You can use quotes instead of / for the regex, to avoid escaping the /:

awk -F: '$6 ~ "^/home/" {print $1}' ~/Desktop/e.txt

With awk, {if (foo) ... } can often be simplified to just foo {...}.

11

You may escape forward slashes as shown below:

awk -F':' '$6~/^\/home\//{ print $1 }' ~/Desktop/e.txt

Another trick would be using complex field separator:

awk -F'[:/]' '$7=="home"{ print $1 }' ~/Desktop/e.txt
  • -F'[:/]' - treat both : and / to be a field separator
1
  • 2
    I believe the comment field (immediately before the home directory) is allowed to contain a slash.
    – user4443
    Nov 7, 2017 at 21:48
8

Assuming e.txt is actually /etc/passwd, you should actually use getent passwd instead of parsing the file since user account information may be stored in multiple locations specified in /etc/nsswitch.conf such as LDAP.

getent passwd | awk -F ':' '$6 ~ "^/home"'

Note that the print statement is implied when the condition is true. That will print the whole line, though. This will print only the user name:

getent passwd | awk -F ':' '$6 ~ "^/home" {print $1}'
4

Alternative for those of us who just don't like awk:

grep -E '^([^:]*:){5}/home/' ~/Desktop/e.txt | cut -d: -f1

Explanation:

  • grep -E enables the use of a few extended regular expression features
  • The expression ([^:]*:) matches one column, including the following ':' separator
  • ^([^:]*:){5} matches the first five columns of each line
  • ^([^:]*:){5}/home/ matches every line where the 6th column starts with '/home/'
  • cut -d: -f1 selects only the first column of its input, using : as column separator
3

You can also use index for fixed string matching like grep -F and use the return value to ensure it matches start of string

$ awk -F: 'index($6,"/home/")==1{print $1}' ip.txt
tom
george
bla
boo

or if regex is needed, pass it as environment variable

$ r='^/home/' awk -F: '$6 ~ ENVIRON["r"]{print $1}' ip.txt
tom
george
bla
boo
3

Just for variety

$ perl -F: -lane 'print $F[0] if m(/home/)' e.txt
tom
george
bla
boo

Explanation

-F:     use ":" as field separator  
-lane
    l   add newline line-ending to every print statement
    a   auto-split input lines into @F array
    n   apply expression line by line to named file (or STDIN)
    e   evaluate perl commands given on command line

print $F[0]   print (only) the first field of each line
if m(/home/)  but only if the line contains the text /home/

Variation if you want to be picky about where /home/ is found

perl -F: -lane 'print $F[0] if $F[5] =~ m(^/home/)' e.txt

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .