--- old/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java 2015-11-26 10:11:04.000000000 +0100 +++ new/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java 2015-11-26 10:11:04.000000000 +0100 @@ -36,10 +36,12 @@ import java.io.OutputStream; import java.io.PrintStream; import java.io.PrintWriter; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.ResourceBundle; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.stream.Collectors; + import jdk.nashorn.api.scripting.NashornException; import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; @@ -201,7 +203,8 @@ // parse options if (args != null) { try { - options.process(args); + final String[] prepArgs = preprocessArgs(args); + options.process(prepArgs); } catch (final IllegalArgumentException e) { werr.println(bundle.getString("shell.usage")); options.displayHelp(e); @@ -232,6 +235,67 @@ } /** + * Preprocess the command line arguments passed in by the shell. This checks, for each of the arguments, whether it + * can be a file name, and if so, whether the file exists. If the file exists and begins with a shebang line, and + * the arguments on that line are a prefix of {@code args} with the file removed, it is assumed that a script file + * being executed via shebang was found, and it is moved to the appropriate position in the argument list. The first + * such match is used. + *

+ * This method canonicalizes the command line arguments to the form {@code -- }, + * where the last of the {@code scripts} is the one being run in shebang fashion. + * + * @param args the command line arguments as passed into Nashorn. + * @return a properly ordered argument list + */ + private static String[] preprocessArgs(final String[] args) { + final List largs = new ArrayList<>(); + Collections.addAll(largs, args); + final List pa = new ArrayList<>(); + String scriptFile = null; + boolean found = false; + for (int i = 0; i < args.length; ++i) { + final String a = args[i]; + final Path p = Paths.get(a); + if (!found && (!a.startsWith("-") || a.length() == 1) && Files.exists(p)) { + String l = ""; + try (final BufferedReader r = Files.newBufferedReader(p)) { + l = r.readLine(); + } catch (IOException ioe) { + // ignore + } + if (l.startsWith("#!")) { + List shebangArgs = Arrays.asList(l.split(" ")); + shebangArgs = shebangArgs.subList(1, shebangArgs.size()); // remove #! part + final int ssize = shebangArgs.size(); + final List filteredArgs = largs.stream().filter(x -> !x.equals(a)).collect(Collectors.toList()); + if (filteredArgs.size() >= ssize && shebangArgs.equals(filteredArgs.subList(0, ssize))) { + scriptFile = a; + found = true; + continue; + } + } + } + pa.add(a); + } + if (scriptFile != null) { + // Insert the found script file name either before a -- argument, or at the end of the options list, before + // any other arguments, with an extra --. + int argidx = pa.indexOf("--"); + if (argidx == -1) { + for (String s : pa) { + ++argidx; + if (s.charAt(0) != '-') { + pa.add(argidx, "--"); + break; + } + } + } + pa.add(argidx, scriptFile); + } + return pa.stream().toArray(String[]::new); + } + + /** * Compiles the given script files in the command line * This is called only when using the --compile-only flag *