As others have pointed out, grep
isn't the best tool for this. If you insist on using it, and if your grep
supports the -o
(only print the matched portion of the line) and -P
(use Perl Compatible Regular Expressions), you can do this:
$ grep -oP '^[^:]+|.*:\K[^:]+(?=:[^:]+)' /etc/password
terdon
/home/terdon
bob
/home/bob
Note that this will print all users, including system users. I am only showing 4 lines as an example.
That will print the user name and home directories of all users but on separate lines. You then need to join each pair of lines to get them together:
$ grep -oP '^[^:]+|.*:\K[^:]+(?=:[^:]+)' /etc/passwd | perl -pe 's/\n/ : / if $.%2'
root : /root
bin : /bin
daemon : /
mail : /var/spool/mail
ftp : /srv/ftp
http : /srv/http
uuidd : /
dbus : /
nobody : /
systemd-journal-gateway : /
systemd-timesync : /
systemd-network : /
systemd-bus-proxy : /
systemd-resolve : /
systemd-journal-upload : /
systemd-coredump : /
systemd-journal-remote : /
terdon : /home/terdon
avahi : /
polkitd : /
colord : /var/lib/colord
rtkit : /proc
gdm : /var/lib/gdm
git : /
bob : /home/bob
Explanation
The regex has two parts, it looks for ^[^:]+
OR (that's what the |
means) .*:\K[^:]+(?=:[^:]+)
. The first looks for one or more non-:
characters from the beginning of the line. This matches the user name. The second part looks for as many characters as possible until a :
(.*:
) and then discards them (that's what the \K
does) so they're not printed. It then matches a string of non-:
which is followed by :
and non-:
. The (?=foo)
construct is called a positive lookahead and is a way of matching the characters after a pattern without including those characters in the match itself.
The perl
command will replace newlines with :
and spaces if the current line number ($.
) is divisible by 2. So, every second line.
/etc/passwd
may or may not be where all the users are. Consider alsogetent passwd
.