(tiny) ls para Windows con Java

Actualmente estoy forzado a trabajar en una máquina con Windows pero cuando llevas algún tiempo trabajando en UNIX o en Linux y se usa la linea de comandos para navegar el sistema de archivos, es mucho, pero mucho muy molesto escribir en automático ls y obtener lo siguiente:

C:\Users\oreyes\java>ls -l
"ls" no se reconoce como un comando interno o externo,
programa o archivo por lotes ejecutable.

Aaaaaaaargggg!!!, las primeras... 255 veces, pasa, pero en la 256 se me acabó la paciencia!!!

Anteriormente había llegado a instalar cygwin pero no esta vez, no quiero todo el stack.

¿Entonces? Hice un programa en Java!

Acá les dejo el código fuente, no se trata de hace un ( mal ) sustituto de ls si no simplemente ayudarme a llenar el hueco.

Además aproveche para poner una "descripción humana" del tamaño de archivos ( en algún shell era una opción como ls -lH pero después ya no lo encontré )

 
package dir;

import java.io.File;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.text.DecimalFormat;

public final class Ls {

    private static final DecimalFormat df = new DecimalFormat("#.##");

    public static void main( String ... args ) {

        File dir =  new File(".");
        for( File f : dir.listFiles() ) {
            print( f );
        }

        System.out.printf(" %6s %2s libres%n",
            df.format( humanSize(  dir.getFreeSpace()  )),
            humanDesc( dir.getFreeSpace() )
        );
    }
    private final static void print( File file ) {
       
        System.out.printf("%td/%1$tm/%1$ty %1$tH:%1$tM %6s %2s %s%s%n",
            new Date(file.lastModified()),
            df.format( humanSize(  file.length()  )),
            humanDesc( file.length() ),
            file.getName(),
            file.isDirectory()?"/":""
        );
    }
    private final static Map<Double,String> sizes = new LinkedHashMap<Double,String>(){{
        put(1024 * 1024 * 1024.0,"gb");
        put(       1024 * 1024.0,"mb");
        put(              1024.0,"kb");
    }};

    private final static double humanSize(long length) {
        for( double l : sizes.keySet() ) {
            if (length > l) {
                return length / l;
            }
        }
        return length;
    }

    private final static String humanDesc(long length) {
        for( double l : sizes.keySet() ) {
            if( length > l ) {
                return sizes.get( l );
            }
        }
        return "   ";
    }
 
}

Luego compilo y creo el jar:

C:\Users\oreyes\java\Ls>javac -d . Ls.java

C:\Users\oreyes\java\Ls>more> manifest.mf
Main-Class: dir.Ls
^C
C:\Users\oreyes\java\Ls>jar -cmf manifest.mf ls.jar dir

Y finalmente creo un .cmd en el directorio que está en mi path:

@echo off
java -jar c:\Users\oreyes\apps\ls.jar

Y listo!!

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de ezamudio

mingw

O puedes instalar mingw y tener ls, rm, cp y un chorro de cosas más

Es como traerse toda la vaca

Es como traerse toda la vaca queriendo un vaso de leche... ¬¬ ( soy malísimo para las analogías )

Yo prefiero(ía) cygwin, pero honestamente, estoy negandome a seguir mucho tiempo más en esta máquina :( :( ( aunque ya llevo 6 meses así )

De cualquier forma, este es solamente para tapar el hueco que mis dedos provocan cada vez que escribo ls -l y me sirvió de pretexto para investigar un poco más del printf ( y sus limitaciones )

Imagen de Shadonwk

eso se llama: "no tener que

eso se llama: "no tener que hacer" dicho con todo respeto jaja lo voy a probar.

Tssss... ni modo... Pero no

Tssss... ni modo...

Pero no , si te fijas es un for y dos printf, lo que ya no me gustó fueron los 6 if's, ( no es mi objetivo escribir poco, más bien mi punto es que no es un programa extenso pues )

Platicando con Rodrigo me acordé de otra cosa que no me gustaba de Cygwin ( no sé si en mingw pasará lo mismo ) pero hacer cd por ejemplo a C:\Users\oreyes era algo como:

cd /cygdrive/c/Users/oreyes

yiack!.. mi problema es que no es un UNIX real :)

Imagen de ezamudio

if's

Si quieres eliminar los if's de tamaños, mete los tamaños a un arreglo y recorre el arreglo para devolver el resultado del primero elemento del arreglo que sea menor que el parámetro:

private final static long tams = new long[]{ 1024 * 1024 * 1024, 1024 * 1024, 1024 };
private final static String tamNames = new String[]{ "GB", "MB", "KB" };
private final static double humanSize(long length) {
  for (long l : tams) {
    if (length > l) {
      return length / l;
  }
  return length;
}
private final static String humanDesc(long length) {
  for (int i = 0; i < tams.length; i++) {
    if (length > tams[i]) {
      return tamNames[i];
    }
  }
  return "   ";
}

Además así luego es fácil meter terabytes, petabytes y cabrobytes, solamente vas metiendo cada nuevo tamaño al principio del arreglo.

Yeap, algo asi estaba

Yeap, algo asi estaba pensando pero supero mi timeout de 10 segundos para pensar como y le puse if's... :)

Por cierto, casualmente, encontré este link hoy de utilerias que hacen lo mismo:

http://www.eskimo.com/~crowley/

EDIT Ya puse el cambio! super bien.

EDIT2 No pude resistir la tentación de usar un mapa en vez de dos arreglos :)

Interesante y divertido

Podria usarse el canRead() y canWrite() para poner el 'xr' y en la primer linea >(e mi caso) le imprimiria la ruta absoluta de la carpeta que se está listando.

Lo usare pero solo por jugar, en la chamba uso windows y no hay necesidad de usar terminal :( y en casa uso mac... bueno para mac le pondre el dir jajaja

mmm, mi forma de esas cosas de los GB, MB KB seria:

        private final static String[] tamNames = {"KB", "MB", "GB"};

        private final static double humanSize (long length) {
                long len = length;
                while ((len / 1024) > 0) len = len / 1024;
                return len;
        }

        final static String humanDesc (long length) {
                long len = length;
                short indexName = -1;
                while ((len = len / 1024) > 0) indexName++;
                return (indexName>=0 && indexName < tamNames.length)?tamNames[indexName] : "   ";
        }

HEEEELP!!

Me pasa algo curioso, detecte un error e mi codigo anterior y tuve que corregirlo pero tuve problemas

E punto es que en:

        private final static double humanSize (long length) {
                long len = length;
                while ((len / 1024) > 0) len = len / 1024;
                return len;
        }

no calculaba bien, pues en los decimales los truncaba, asi que hice esto:

        private final static double humanSize (long length) {
                double len = length;
                while ((len / 1024.0) >= 0.1) len = len / 1024.0;
                return len;
        }

pero tuve que ponerle ">=0.1" en la cndicion porque si le dejaba el ">0.0" o simplemente ">0" siempre daba true aunque el valor de lavariable (que vi e e debug) fuese por ejemplo 0.9547522 y asi hasta llegar a "8.9E-323"

Que estara pasando??

Imagen de ezamudio

doubles

Los doubles tienen broncas, ya hemos hablado de eso en otros posts.

Lo que pasa es que division

Lo que pasa es que division entre enteros ( incluyendo long ) da entero.

Entonces 123081230918230 / 1024 va da siempre un numero cerrado.

Lo único que tenías que hacer es dividir entre 1024.0 ( osea mil venticuatro punto cero (1024 como double) ) y toda la operación se hace double.

Al memento de presentar en ves de 1.981298347123 se usa el formatter como sugirió Enrique y listo.

Le puse inicialmente el directorio actual y aquello de rwx pero el directorio actual se repetía inmediatamente algo como:

c:\Users\oreyes\>ls -l
Directorio  c:\Users\oreyes
etc.
ec

Entonces por eso se lo quité, pero serviría cuando se le pase un argumento:

C:\Users\oreyes\> ls -l C:\Otro\Path
Directorio C:\Otro\Path
..

Sobre lo de rwx se lo quité por que en mi filesystem absolutamente todos decian que sí y resultaba redundante:

   ... printf("%s%s%s", file.canRead()?"r":"-",file.canWrite()?"w":"-",file.canExecute()?"x":"-");

Esta bueno como ejercicio no?

:)

Error mio, me dormi bien gacho!!

Bueno, en este caso el error fue mio, la verdad que me dormi y bien gacho, desde un principio debio haber sido >=1.0 y nunca hubo broncas con los doubles mas bien andaba mal mi algoritmia de calculo pues los doubles nunca fueron menor a 0, me daba valores diminutos pero nunca menores o igual a 0 (y yo me confundi con ese signo de -.bueno, esto finalmente quedo asi:

        private final static String[] tamNames = {"KB", "MB", "GB"};

        private final static double humanSize (long length) {
                double len = length;
                while ((len / 1024.0) >= 1.0) len = len / 1024.0;
                return len;
        }

        final static String humanDesc (long length) {
                long len = length;
                short indexName = -1;
                while ((len = len / 1024) > 0) indexName++;
                return (indexName>=0 && indexName < tamNames.length)?tamNames[indexName] : "   ";
        }

MORALEJA: "0.9547522" y "8.9E-323" SON POSITIVOS Y "CLARO QUE" MAYORES A 0

Ja ja ja a mi me pasa cuando

Ja ja ja a mi me pasa cuando programo con una /cerveza+/ al lado... híjole... ya es vieeeeeeeeeernes.. ( por si alguien no lo sabía )

¿No era mejor haber tecleado

¿No era mejor haber tecleado dir o cómo te han dicho instalar cygwin o msysgit?...En fin cada quien XD.

¬¬ Lo que pasa es que ...

Ja ja ja ... claro que era más fácil lo que pasa es que ...

... bueno en resumen ( para no escribir de nuevo todo ) las interfaces de usuario crean hábitos ( por ejemplo cuando alguién le da click a un cuadro de dialogo sin leerlo solamente por el hecho de que nos acostumbramos a dar click en cualquier popup )

Estos hábitos ( como todos los hábitos pues ) son muy dificiles de quitar, llegan hasta el punto de ser ejecutados en automático ( mi cerebro ya no piensa en "dir" ni en "ls -l", simplemente mis dedos lo hacen y ya, justo de la misma manera que yo ya no pienso en poner un pie frente al otro al caminar )

Sí se puede instalar otras utilerías pero en este caso era demasiado (para mi). Por ejemplo el comando "mv" tiene su equivalente de "move" , "rm" "erase" y etc. con esos no tengp problema, porque no estoy habituado.

Lo único que pretendo es tapar el hueco que mi hábito creó

Re: ¬¬ Lo que pasa es que ...

¿Y no era más fácil buscarle en google: "alias windows"? (Véase primer resultado). XD++. NTC, es un juego, está chido el aporte.

Pues claro que sí

>¿Y no era más fácil buscarle en google: "alias windows"?

Pero ¿que te hace pensar, que si se me olvida escribir "dir" será más fácil escribir:

doskey ls=dir

antes de cada sesión?

Claro, puedo buscar también como simular un .profile o .bashrc en windows para que el comando se ejecute al abrir la consola ¬¬

update Ahora que pensándolo todavía más, lo que sí es mucho más fácil es crear un comando: ls.cmd

dir .
Imagen de ezamudio

EJERCICIO

Tómenlo como un ejercicio de programación, es didáctico. Es como lo del ciclo ese para reemplazar unas letras agregando cada vez más X y a ver quién lo hacía en menos líneas.

Imagen de Sr. Negativo

Windows

Según yo, hubiese sido más fácil aprenderse los comandos MS-DOS. Pero tu iniciativa fue buena.

Tranqui tronco Oscar, era

Tranqui tronco Oscar, era dentro del cotorreo; ya te dije que estuvo buena la aportación jeje.

¬¬

¬¬

Yo pienso que

Yo pienso que estuvo chido, a mi me gusto leer este post y ademas hoy tuve la necesidad de implementar el calculo humano del taaño de archivos en mi chamba... asi que pues que mejor que decirle a Oscar: Gracias canijo!
...
HE DICHO!

Je je :) Entiendo bien los

Je je :)

Entiendo bien los comentarios, yo mismo los haría; habiendo tantas cosas disponibes ( incluso quizá lo he dicho ), "Para que reinventar la rueda, mejor esto, o aquello" pero también he dicho que estas cosas son buenas para aprender ( para que yo mismo aprenda ).

Pero yeap, es como una Kata de programación ( no me gusta nada el término pero es el más conocido ) para ejercitarme :)

Imagen de Sr. Negativo

Esto me recuerda...

Que hace tiempo estuve haciendo una pequeña aplicación tipo alarma de despertador o calendario algo asi,y no me quedaba por más que trataba.

Según yo tenia que programarse un día, un mes, un año dependiendo la necesidad del usuario y automáticamente hacer un "beep" y mandar un mensaje con el recordatorio.

Estuve leyendo libros y manuales tratando de encontrar ayuda y casi volviendome loco. Al final no me quedo otra que usar las herramientas de MS Outlook.

Creo que nos gusta complicarnos la vida.

Wweeeeeno, a mi me tomó 20

Wweeeeeno, a mi me tomó 20 minutos ( bueno 30 entre revisar el foro y esperar a que levantara el servidor ) en hacer esto...

Pero sí, la naturaleza humana es así.

Imagen de Nopalin

Señor negativo, pero eso es

Señor negativo, pero eso es lo bueno, tal vez no conseguiste tu objetivo, pero estoy seguro que aprendiste cosas extras que en algun futuro te van a ser de utilidad, chance y hasta hubieras mejorado el sistema de alarmas del outlook, uno nunca sabe.

Pero si siempre te vas a ir por el camino fácil sin aprender los cimientos, entonces eres de los que no saben para que es el javac *.java y eso no es bueno.

sobres

Imagen de ezamudio

ls.groovy

La versión del programa pero en Groovy, con los cambios que sugerí de tener dos arreglos en vez de un mapa:

import java.text.DecimalFormat

DecimalFormat df = new DecimalFormat('#.##')
List sizes = [ 1024*1024*1024.0, 1024*1024.0, 1024.0 ]
List descs = [ 'gb', 'mb', 'kb' ]

def humanSize = { length ->
    def l = sizes.find { length > it } ?:1
        length / l
}

def humanDesc = { length ->
    def d = null
    sizes.eachWithIndex { l, i ->
        if (!d && length > l ) {
            d = descs[i]
        }
    }
    d?:"   "
}

def printFile = { File file ->
    System.out.printf('%td/%<tm/%<ty %<tH:%<tM %6s %2s %s%s%n',
        new Date(file.lastModified()),
        df.format( humanSize(  file.length()  )),
        humanDesc( file.length() ),
        file.name,
        file.isDirectory()?"/":""
    )
}

File dir =  new File(args.length > 0 ? args[0] : ".")
dir.listFiles().each {
    printFile( it )
}
System.out.printf(" %6s %2s libres%n",
    df.format( humanSize(  dir.getFreeSpace()  )),
    humanDesc( dir.getFreeSpace() )
)

Orale si está muy chido. Voy

Orale si está muy chido.

Voy a mover acá lo de Scala para no contaminar el otro thread

Nomás por practicar hice ( casi ) el mismo programa de Ls en Scala, usando lo que java.daba.doo sugirió: un while.

Por alguna razón siempre me da 0 cuando formateo el número. En fin.

Ahi se los dejo con alguna anotaciones para que lo comparen y sufran la belleza de Scala ( a mi nomás no me termina de gustar. Nota voy aprendiendo Scala y estoy seguro que hay al menos otras 6 formas de hacer esto mismo )

import java.io.File
import java.util.Date
import java.text.DecimalFormat

object Ls {

    // implicit conversion son como extension methods en C# pero más poderosos ( según)
    implicit def f(f:File) = new {
        // Los parentesis pueden ser omitidos
        // por lo que: f.getName es lo mismo que f.getName()
        // y humanSize lo mismo que humanSize()
        def printFile() {
            printf("%td/%1$tm/%1$ty %1$tH:%1$tM %6s %2s %s%s%n",
                new Date( f.lastModified ),
                df.format( humanSize ),
                humanDescription,
                f.getName,
                if ( f.isDirectory() ) "/" else "" // ternario en Scala
            )
        }
        val df = new DecimalFormat("#.##")
        def humanSize() : Double  = {
            var len = f.length * 1.0
            while( len / 1024.0  > 0.0 ){
                len = len / 1024.0
            }
            len // no se requiere escribir "return"
        }
        val sizeName = Array("kb","mb","gb")
        def humanDescription() : String = {
            var l = f.length
            var i = 0
            while(  l / 1024 > 0 ) {
                l = l / 1024
                i+=1
            }
            sizeName(i) // tratando el arreglo como una funcion ( se invoca apply por debajo )
        }
    }

    def main( args: Array[String] ) {
        // el "_" es como el "it" en Groovy
        new File(".").listFiles.foreach( _.printFile )
    }
}

Imagen de rodrigo salado anaya

Mini contribución a (tiny) ls

Pues jugado un ratito (lo que son las cosas en linux), no me gusta como sale el separador \, así que solo le puse al método private final static void print( ... ) :

...
file.isDirectory() ? File.separator : "");
...

así ya lo saca bonito con el separador que debe =).

Alternativa