Esto es Java usado como lenguaje de guiones: un archivo, sin proyecto ni Maven, ejecutable con java Contar.java. Cuenta líneas, palabras y caracteres, como wc, y de paso saca las palabras más repetidas.
También te puede interesar
Lo que hace
salida$ java Contar.java texto.txt
3 13 62 texto.txt
$ java Contar.java texto.txt otro.txt
3 13 62 texto.txt
1 3 15 otro.txt
$ java Contar.java -l texto.txt
3 texto.txt
$ cat texto.txt | java Contar.java
3 13 62 (entrada estándar)
salida$ java Contar.java -t texto.txt
texto.txt
duerme 2
gato 2
casa 1
corre 1
grande 1
La forma del programa
void main(String[] args) {
try { System.exit(ejecutar(args)); }
catch (Exception e) { System.err.println("contar: " + e.getMessage()); System.exit(1); }
}
int ejecutar(String[] args) throws IOException { ... }
Toda la lógica está en un método que devuelve el código de salida y deja subir las excepciones; main es lo único que llama a System.exit. Esto importa por dos razones: System.exit en mitad del programa se salta los finally y no cierra nada, y un método que devuelve un int se puede probar desde una prueba automática, cosa que un main que llama a exit no.
Las opciones
for (int i = 0; i < args.length; i++) {
switch (args[i]) {
case "-l" -> lineas = true;
case "-p" -> palabras = true;
case "-c" -> caracteres = true;
case "-t" -> top = true;
case "-h", "--ayuda" -> { System.out.println(USO); return 0; }
default -> {
if (args[i].startsWith("-")) { System.err.println("contar: opción desconocida " + args[i]); return 2; }
rutas.add(args[i]);
}
}
}
if (!lineas && !palabras && !caracteres && !top) { lineas = palabras = caracteres = true; }
Esa última línea es el detalle que hace que la herramienta se sienta bien: sin opciones, muestra las tres cuentas. Un programa que no hace nada cuando lo llamas sin argumentos es un programa que no has terminado.
static final String USO = """
uso: contar [-l] [-p] [-c] [-t] [archivo...]
-l solo líneas -c solo caracteres
-p solo palabras -t las 5 palabras más repetidas
sin archivos lee de la entrada estándar; sin opciones muestra las tres cuentas""";
Archivos o entrada estándar
if (rutas.isEmpty()) { // sin archivos: la entrada estándar
try (var r = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8))) {
informar("(entrada estándar)", r.lines().toList(), ...);
}
return 0;
}
int fallos = 0;
for (String ruta : rutas) {
Path p = Path.of(ruta);
if (!Files.isReadable(p)) { System.err.println("contar: no puedo leer " + ruta); fallos++; continue; }
informar(ruta, Files.readAllLines(p, StandardCharsets.UTF_8), ...);
}
return fallos == 0 ? 0 : 1;
Dos costumbres de Unix que conviene respetar: si no hay archivos, lee de la entrada estándar —eso es lo que permite encadenar con tuberías—, y si un archivo falla, sigue con los demás y devuelve error al final, en vez de abandonar en el primero.
System.err y los resultados a System.out. Así java Contar.java *.txt > cuentas.txt deja el informe limpio en el archivo y los errores siguen viéndose en la pantalla.Los códigos de salida
echo $?-h -> 0 archivo bueno -> 0 archivo ausente -> 1 opción mala -> 2
Cero cuando todo va bien —incluida la ayuda, que no es un fallo—, 2 para «me has llamado mal» y 1 para «lo intenté y no pude». Es lo que espera cualquier guion que compruebe el resultado.
Usarla a diario
# 1. tal cual, sin compilar nada java Contar.java -t informe.txt # 2. como orden del sistema, con la línea mágica al principio del archivo #!/usr/bin/env java chmod +x contar && ./contar -t informe.txt # 3. compilada y empaquetada, para que arranque más rápido javac -d clases Contar.java jar --create --file contar.jar --main-class Contar -C clases . java -jar contar.jar -t informe.txt
La primera forma recompila en memoria cada vez: para un archivo pequeño eso es una fracción de segundo, y a cambio no deja nada. La segunda, con #!/usr/bin/env java como primera línea, convierte el .java en un ejecutable más del sistema. La tercera es la de siempre, y es la que conviene si la herramienta se llama mil veces desde otro guion.
Y si algún día esto crece hasta tener subórdenes y veinte opciones, lo que toca es picocli y un proyecto de verdad, como el de la última lección del curso.
Todo el código se compiló y se ejecutó con Java 25 LTS en un contenedor limpio antes de publicar esta página; las salidas están copiadas de esa ejecución.