Read /etc/passwd with Java8 streams

This article shows how to read and parse the /etc/passwd file with the new Java8 stream API and Lamda functions

2 Min. Lesezeit Aktualisiert 03.05.2017

The Problem

As a programmer I am always searching for elegant ways how to accomplish certain things. One of the last tasks was to parse the /etc/passwd file of a linux box. Of course there are multiple ways how to do this, but the Java8 way seems to be a cool solution.

The Solution

The Program consists of two classes. The first one is the ‘PwdUser’ class which represents one single entry in the passwd file:

public class PwdUser 
{
 public String userid;
 public String password;
 public Integer uid;
 public Integer gid;
 public String fullname;
 public String homedir; 
 public String shell;
 
 public PwdUser(String pwdline)
 {
  String[] values = pwdline.split(":");
  
  if(values.length != 7)
  {
   throw new IllegalArgumentException("Line must have 7 fields");
  }
  
  this.userid = values[0];
  this.password = values[1];
  this.uid= Integer.parseInt(values[2]);
  this.gid= Integer.parseInt(values[3]);
  this.fullname = values[4];
  this.homedir = values[5];
  this.shell = values[6];
  
 }
}

As you see - you can call the constructor with a full line from the passwd file ant the line is slitted up into separate variables. (Errorchecking could be better - but this example is more for educational purposes)

The second class is the main class:

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Test 
{

 public static void main(String[] args) 
 {
  // Read the file
  try (Stream<String> stream = Files.lines(Paths.get("/etc/passwd"))) 
  {
   // Yeah - Java8 read, filter and instantiante on one line of code !
   ArrayList<PwdUser> users = stream.map(PwdUser::new)
     .filter(u -> !u.shell.equals("/bin/false"))
     .filter(u -> !u.shell.equals("/usr/sbin/nologin"))
     .collect(Collectors.toCollection(ArrayList::new));

   // Do something with your users ...
   for (PwdUser u : users) 
   {
    System.out.println(u.userid);
   }
  } 
  catch (Exception e) 
  {
   e.printStackTrace();
  }

 }
}

This is where the magic happens:

  1. The file is opened within the try - which means that the JVM closes it automatically (try-with-ressources)
  2. Within the try block we have the stream of the file which is processed as follows:
    1. Create a PwdUser for every line in the file
    2. Remove all objects which are not allowed to log into the system (This is indicated by a shell does not equal to /bin/false and also not to /usr/sbin/nologin
    3. Grab the remeaning ones an put them into a new ArrayList
  3. Iterate over the ArrayList and process the objects

Neat - isn’t it ?