Java 11: Выполнение исходного файла через Shebang не работает

Я хотел проверить некоторые новые функции java 11, которые были выпущены два дня назад. В JEP 330 указано, что я могу запустить Java-Source-Code-Program без компиляции. Он также должен поддерживать использование файлов Shebang-Files.

Поэтому я написал эту небольшую программу Hello-World Test.java:

#!/opt/java/jdk-11/bin/java --source 11

public class Test
{
    public static void main(String[] args)
    {
        System.out.println("Hello World!");
    }
}

Я загрузил JDK 11 и извлек его в /opt/java. Следовательно, сам Шебанг работает. Т.е. выполнение /opt/java/jdk-11/bin/java --version дает мне

openjdk 11 2018-09-25
OpenJDK Runtime Environment 18.9 (build 11+28)
OpenJDK 64-Bit Server VM 18.9 (build 11+28, mixed mode)

После выполнения Test.java исполняемого файла (используя chmod +x Test.java) выполнение не выполняется. Т.е. ./Test.java дает мне:

./Test.java:1: error: illegal character: '#'
#!/opt/java/jdk-11/bin/java --source 11
^
./Test.java:1: error: class, interface, or enum expected
#!/opt/java/jdk-11/bin/java --source 11
^
2 errors
error: compilation failed

Как только я Test.java Shebang-Line из Test.java и запускаю его с помощью /opt/java/jdk-11/bin/java --source 11 Test.java все работает как шарм, и я получаю ожидаемый результат: Hello World!

Моя машина работает Ubuntu 17.04. Я связал javac с тем из JDK 11 (т. javac -version Выполнение javac -version дает javac 11).

Ответ 1

Имя файла не должно заканчиваться на .java чтобы исполняемый файл java игнорировал строку shebang. Вы можете использовать другое расширение или просто не иметь расширения вообще (это то, что они делают в примере JEP, и это то, что я бы рекомендовал).

Из JEP 330 (выделено мной):

Когда пусковая установка считывает исходный файл, если файл не является исходным файлом Java (т.е. Это не файл, чье имя заканчивается на.java), и если первая строка начинается С# !, то содержимое этой строки до, но не включая первую новую строку, игнорируются при определении исходного кода, который должен быть передан компилятору. Содержимое файла, которое появляется после первой строки, должно состоять из допустимого CompilationUnit, определенного в § 3.7 в редакторе спецификации языка Java, соответствующей версии платформы, указанной в опции --source, если она присутствует, или версию платформы, используемой для запуска программы, если параметр --source отсутствует.

Не нужно заканчивать ".sh" конкретно; также, что потенциально вводит в заблуждение, потому что файл на самом деле не является скриптом оболочки.

Ответ 2

Несколько проб и ошибок дали мне правильное решение. Это .java расширением файла .java.

Т.е. если я переименую файл в Test.sh все будет работать.

Вот полный пример Hello-World-Shebang-Example:

Создайте файл Test.sh с контентом, подобным

#!/opt/java/jdk-11/bin/java --source 11

public class Test
{
    public static void main(String[] args)
    {
        System.out.println("Hello World!");
    }
}

Сделайте его исполняемым (т. chmod +x Test.sh).

И последнее, но не менее важное значение выполняет его использование ./Test.sh

Ответ 3

Согласно JEP, с которым вы связались (см. Раздел файлов shebang), файл shebang должен использоваться для запуска java-процесса, а не для использования в качестве параметра для java:

Файл shebang для запуска Java-пусковой установки с использованием режима исходного файла должен начинаться с чего-то вроде:

#!/path/to/java --source version

Например, мы могли бы взять исходный код для программы Hello World и поместить его в файл с именем hello после начальной строки #!/Path/to/java --source 10, а затем пометить файл как исполняемый файл. Затем, если файл находится в текущем каталоге, мы можем выполнить его с помощью:

$./hello

Другими словами, то, что вы хотите сделать, скорее сделает исполняемый файл Test.java. Вам также нужно будет переименовать его, так как он не будет работать как shebang и полосу первой строки, когда он будет называться *.java.

$ move Test.java test
$ chmod +x test
$ ./test 

Это запустит процессор shebang, который разделит первую строку и передаст остальную часть скрипта в /path/to/java, а Java скомпилирует сценарий и запустит основной метод.