Search code examples
javascriptrhino

runCommand returns a value, but the result cannot be assigned to a variable


The following is a short function to return a file size on a Linux system, run in the rhino shell:

function fsize(file){
    var filesize = runCommand("stat","-c %s",file);
    return filesize;
} 

Running the function returns a value; e.g:

fsize('/etc/hosts'); 

returns a file size in bytes

But if I run:

var filesize = fsize(testfile);

the var filesize is "0" when output to the console.

Why is this happening, and how can it be fixed?

To examine variables, I used the function:

function output(strings){
    print(strings);
}

A sample shell session, showing output:

js> var file = "/var/www/javascript/ProgressMon/progressmon.js"
js> fsize(file);
683
0
js> var filesize = fsize(file);
683
js> filesize;
0
js> output(filesize);
0
js> 

Solution

  • Examining the runCommand documentation, it can be called in the following forms:

    runCommand(command);
    runCommand(command, arg1, ..., argN);
    runCommand(command, arg1, ..., argN, options);
    

    The sample uses the second form, which prints the output to the terminal but does not capture it in any way that's available to code. In other words, fsize(testfile) does not return the file size, it prints it.

    The result returned by all forms is the exit status of the command, which is what gets assigned to filesize.

    To capture output, you must use the third form and pass an object with an output property, which can be an java.io.OutputStream or a string. In this case, you probably want the latter, as that will cause program output to be appended to the property. The function can then call parseInt on the output to get the size as a number, rather than a string.

    The system call might generate errors. To handle them within fsize, you could print error messages and return a negative value to indicate an error. If runCommand might throw an exception, the code could be wrapped in a try-catch block.

    function fsize(file){
        var options = {
            output: '',
        };
        try {
            var result = runCommand("stat", "-c", "%s", file, options);
            if (result) {
                print(options.err);
                return -1;
            } else {
                return parseInt(options.output);
            }
        } catch (err) {
            print(err);
            return -1;      
        }
    } 
    

    Alternatively, you could let code up the call-stack handle exceptions, and raise an exception for any error generated by the runCommand call .

    function fsize(file){
        var options = {
            output: '',
            err: '',
        };
        if (runCommand("stat", "-c", "%s", file, options)) {
            // note: `SystemError` must be defined, as it's not a standard class
            throw new SystemError(options.err);
        }
        return parseInt(options.output);
    } 
    

    Note that instead of calling output(filesize); to print the value of filesize, you can evaluate it directly:

    js> var filesize = fsize(file);
    js> filesize
    683