Lazy Diary @ Hatena Blog

PowerShell / Java / miscellaneous things about software development, Tips & Gochas. CC BY-SA 4.0/Apache License 2.0

ProcessBuilder freezes when you try to run a .bat containing PowerShell scripts

Background

By using the .bat file from here, you can run a PowerShell script by just click the .bat file.

reosablo.hatenablog.jp

Problem

When you are trying to run the .bat file from Java ProcessBuilder class like this, the .bat file will never finish and ProcessBuilder will freeze.

package org.example;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class Main {

    public static void main(String[] args) throws IOException, InterruptedException {
        List<String> list = new ArrayList<String>();
        list.add("c:/tmp/hoge.bat");
        ProcessBuilder pb = new ProcessBuilder(list);
        Process p = pb.start();
        p.waitFor();
        int returnValue = p.exitValue();
        System.exit(returnValue);
    }
}

You can run the .bat file from the command prompt correctly.

Cause

The PowerShell script in .bat file uses the $input automatic variable. It reads input from STDIN, so it will wait for input from STDIN.

You can reproduce this problem with a .bat file like this:

powershell.exe -Command "$input"

Solution

  • You can use another .bat file that doesn't use $input: www.pg-fl.jp
  • If you want to use PSCommandPath and $PSScriptRoot in the PowerShell script, you can do like this:
@setlocal enableextensions enabledelayedexpansion & set "THIS_PATH=%~f0" & PowerShell.exe -Command "& (iex -Command ('{$PSCommandPath=\"%~f0\"; $PSScriptRoot=\"%~dp0"; #' + ((gc '!THIS_PATH:'=''!') -join \"`n\") + '}'))" %* & exit /b !errorlevel!
  • You have to close STDIN when a PowerShell script in .bat file uses $input like:
package org.example;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class Main {

    public static void main(String[] args) throws IOException, InterruptedException {
        List<String> list = new ArrayList<String>();
        list.add("c:/tmp/hoge.bat");

        ProcessBuilder pb = new ProcessBuilder(list);
        Process p = pb.start();
        p.getOutputStream().close();   // Close STDIN
        p.waitFor();
        int returnValue = p.exitValue();
        System.exit(returnValue);
    }
}