Всем привет! В продолжении публикации о возможностях PowerShell, упомянул, что выделю в отдельную статью использование библиотеки Selenium с упомянутым языком. Этот небольшой гайд также может подойти для понимания, что из себя представляет данная библиотека и общее представление, как с ней работать, т.к. не зависимо от выбранного вами языка, принцип работы с Selenium одинаковый. Сразу отвечу на вопрос для тех, кто не знает, что это за библиотека или кому она может понадобиться. Selenium в первую очередь инструмент для функционального тестирования, это когда нужно проделать ряд действий имитируя реального пользователя и убедиться, что функционал Web-приложения работает исправно или неисправно. Реже используется для автоматизации различных задач в браузере (вызов JavaScript функций по средствам нажатий кнопок, заполнение форм и т.п.), для которых не предусмотрено API, а в некоторых случаях может выступать более удобной и полноценной альтернативой. Работая системным администратором, мне было удобно автоматизировать действия в панелях управления различных сервисах, или собирать специфические метрики с отправкой в базу данных. Selenium даже может входить в список инструментов DevOps-инженера. На мой взгляд, инструмент очень интересный с точки зрения творческого подхода к решению различных задач.
Постараюсь разложить все по полочкам, от установки всех зависимостей до примера работы с данным инструментом, также продемонстрирую альтернативный инструмент, который использовал до знакомства с Selenium. Специально для статьи подготовил модуль, который позволяет общаться с бесплатной версией ChatGPT из консоли PowerShell, который вы можете установить, используя всего две команды, а так же покажу, как просто создать такой модуль. Хочу заметить, что осознанно выделяю данную статью в средний уровень сложности Habr, по итогам ознакомления, для людей базово знакомых с PowerShell (для этого у меня есть отдельная работа с заметками), порог вхождения будет минимален. На просторах интернета не так много информации об использовании Selenium с языком PowerShell, я же узнал о данном инструменте из этой статьи, к слову, на этом ресурсе много полезного подчеркнул для себя в процессе изучения PowerShell, но несколько важных нюансов для построения собственных модулей там не было раскрыто. Также ключевой проблемой являлась установка всех зависимостей актуальной версии для дальнейшего масштабирования решений с использованием данной библиотеки, стоит упомянуть модуль selenium-powershell на GitHub, который не поддерживается с 2019 года.
Что требуется для работы:
Браузер. Вариантом несколько: Internet Explorer, Google Chrome, Mozilla Firefox. Важным моментом для меня было использовать конкретный браузер, базирующийся на Chrome.
ChromeDriver. Данный драйвер нужен для возможности Selenium управлять браузером (драйвер может быть другой, в зависимости от выбранного вами браузера). Важным моментом (и изначальной проблемой) является соответствие версии драйвера и текущей версии установленного браузера. За несколько месяцев использования я заметил тенденцию, что версия Google Chrome всегда опережает Chrome Driver на порядок релизов, из-за этого использовать драйвер с текущей версией установленного вами браузера не получится, т.к. он обновляется автоматически в целях поддержания безопасности. В поисках возможности автоматизировать данный процесс, где для сначала нужно удалить браузер из системы (тут вариантом несколько, через WMI/CIM или запускать деинсталлятор в тихом режиме) а потом устанавливать конкретную версию. Удобнее всего управлять данным процессом через менеджер пакетов, например, NuGet, Chocolatey, WinGet или Scoop. Изначально я пошел по этому пути, но тут возникали проблемы, браузер мог самостоятельно обновиться несмотря на различные запреты, а иногда вовсе отсутствовала возможность установки подходящей версии, со временем натыкаясь на различные ошибки в работе, к тому же, этот подход не позволял обновлять сразу все драйверы и приходилось производить много ручных действий. Спустя время меня посетила идея использовать Chromium, после чего весь процесс развертывания получилось очень сильно упростить и никаких проблем уже не возникало, так как у браузера имеется портативная версия. Самое приятное, Chromium имеет открытый исходный код и разрабатывается сообществом, что позволяет использовать такое решение в организации на территории РФ по сей день.
WebDriver. Драйвер Selenium. Так как для работы с драйвером мы используем язык PowerShell, который в свою очередь базируется на платформе .NET, то нам нужна версия для C# (всего вариантов несколько, поддерживаются такие языки, как Python, Java, JavaScript и Ruby). Также присутствует библиотека WebDriver.Support, которая нужна для обработки реже используемых действий.
Процесс установки и обновления вышеперечисленного у меня получилось автоматизировать. Если кратко, то для быстрого развертывания всех зависимостей достаточно воспользоваться одной командой в терминале, которая в свою очередь считает из репозитория GitHub PowerShell скрипт и запустит его в вашей системе:
Invoke-Expression(New-Object Net.WebClient).DownloadString("https://raw.githubusercontent.com/Lifailon/Deploy-Selenium/rsa/Deploy-Selenium-Drivers.ps1")Данный скрипт установит по пути “домашняя директория пользователя/Документы/Selenium” портативную версию браузера Chromium, chromedriver.exe, WebDriver.dll и WebDriver.Support.dll актуальных версий на текущий момент, а при повторно запуске, обновит все файлы.
Для начала работы определимся с шаблоном, который будем использовать при написании любого скрипта в дальнейшем. В начале необходимо загрузить установленные ранее зависимости:
$path = "$home\Documents\Selenium\"
$log = "$path\ChromeDriver.log"
$ChromeDriver = "$path\ChromeDriver.exe"
$WebDriver = "$path\WebDriver.dll"
$SupportDriver = "$path\WebDriver.Support.dll"
$Chromium = (Get-ChildItem $path -Recurse | Where-Object Name -like chrome.exe).FullName
Add-Type -Path $WebDriver
Add-Type -Path $SupportDrivertry { $ChromeOptions = New-Object OpenQA.Selenium.Chrome.ChromeOptions # создаем объект с настройками запуска браузера $ChromeOptions.BinaryLocation = $Chromium # передаем путь до исполняемого файла, который отвечает за запуск браузера $ChromeOptions.AddArgument("start-maximized") # добавляем аргумент, который позволяет запустить браузер на весь экран $ChromeOptions.AcceptInsecureCertificates = $True # игнорировать предупреждение на сайтах с не валидным сертификатом #$ChromeOptions.AddArgument("headless") # скрывать окно браузера при запуске $ChromeDriverService = [OpenQA.Selenium.Chrome.ChromeDriverService]::CreateDefaultService($ChromeDriver) # создаем объект настроек службы драйвера $ChromeDriverService.HideCommandPromptWindow = $True # отключаем весь вывод логирования драйвера в консоль (этот вывод нельзя перенаправить) $ChromeDriverService.LogPath = $log # указать путь до файла с журналом $ChromeDriverService.EnableAppendLog = $True # не перезаписывать журнал при каждом новом запуске #$ChromeDriverService.EnableVerboseLogging = $True # кроме INFO и ошибок, записывать DEBUG сообщения $Selenium = New-Object OpenQA.Selenium.Chrome.ChromeDriver($ChromeDriverService, $ChromeOptions) # инициализируем запуск с указанными настройками
# Далее следует код для обработки действий в браузере
}finally { $Selenium.Close() $Selenium.Quit()
}Теперь определяемся, что мы хотим выполнить в браузере. Для примера, поставим задачу, где мы откроем онлайн калькулятор и посчитаем сумму двух чисел, а полученный результат выведем на экран консоли. Работа с Selenium всегда начинается с перехода по нужной нам ссылке (url), откуда будем начинать выполнять наши действия, для этого используем дочерний метод навигации – GoToUrl:
$Selenium.Navigate().GoToUrl("https://google.com")Если в опциях запуска браузера мы не используем аргумент headless (достаточно его закомментировать), что удобно при отладке (мы можем наблюдать все выполняемые нами действия в окне браузера или наоборот), то в браузере мы увидим, как перейдем по указанной нами ссылке, в нашем случае поисковика Google. Далее нужно передать текст для поиска, например так: “calculator online”. Для этого нужно найти элемент, который отвечает за ввод текста.
Поиск элементов является ключевым моментом при работе с данным инструментом, этот алгоритм аналогичен для всех дальнейших действий. Можно использовать два способа:
1. DevTools – это встроенный набор инструментов Web-разработчиков и тестировщиков. В своем браузере (для удобства в отдельном окне, например, Google Chrome) переходим по нужной нам ссылки и запускаем данный инструмент используя клавишу F12 (или правой кнопкой мыши на странице – Просмотреть код), где нам нужна первая вкладка – «Элементы» (еще иногда может быть полезна вкладки Сеть, что бы отслеживать образующиеся запросы во время взаимодействия с приложением). Универсальным способом будет использовать комбинацию Ctrl+Shift+C, далее используя курсор мыши наводим на нужный нам элемент, в нашем случае, поисковой строки:
Выбранный элемент поисковой строки Google.
Что нас тут может интересовать:
textarea – это элемент управления (он же Tag), который предоставляет пользователю возможность вводить несколько строк текста.
jsname=”yZiJbe” – это атрибут, который указывает на используемый JavaScript-код для данного элемента. В нашем случае может использоваться для поиска через метод
CssSelector.id=”#APjFqb” – это уникальный идентификатор элемента – селектор (selector), который можно скопировать правой кнопкой мыши, и использоваться для обращения к этому элементу из JavaScript, CSS, а также в нашем случае из PowerShell. Альтернативным вариантом является XPath, который также можно использовать для обращения к элементу.
class=”gLFyf” — атрибут, который определяет класс элемента, который можно использовать для поиска через
ClassName.name=”q” — атрибут, определяющий имя элемента, которое будет отправлено на сервер при отправке формы, его можно использовать для обращения к элементу по имени.
maxlength=”2048″ — атрибут, указывающий на максимальное количество символов, которые пользователь может ввести в данном поле.
rows=”1″ — атрибут, который определяет количество строк текстового поля ввода.
aria-label=”Найти” — лейбл или этикетка, который соответствует свойству объекта
ComputedAccessibleLabelи может быть полезен при фильтрации элементов, используя методFindElements.role=”combobox” — роль элемента, соответствует свойству объекта
ComputedAccessibleRole.
Исходя из полученных данных, можно составить несколько запросов, которые будут выполнять одинаковое действие, т.е. искать нужный нам элемент с строкой для ввода текста.
Проще всего обращаться к таких элементам по его
Id:
$Selenium.FindElements([OpenQA.Selenium.By]::Id('APjFqb'))Если скопировали
XPath:
$Selenium.FindElements([OpenQA.Selenium.By]::XPath('//*[@id="APjFqb"]'))Или по имени элемента:
$Selenium.FindElements([OpenQA.Selenium.By]::Name('q'))В формате XPath, аналогичный запрос будет выглядеть следующим образом (используя соответствующий метод):
$Selenium.FindElements([OpenQA.Selenium.By]::XPath('//*[@name="q"]'))Используя имя класса:
$Selenium.FindElements([OpenQA.Selenium.By]::ClassName('gLFyf'))Используя
jsnameдля CSS селектора:
$Selenium.FindElements([OpenQA.Selenium.By]::CssSelector('[jsname="yZiJbe"]'))2. Второй вариант, если вы не хотите пользоваться браузером для поиска элементом (мне иногда так было удобнее), а также если у элемента нет уникальных данных (когда в DevTools мы можем увидеть только название тэга, такое будет в примере с ChatGPT ниже) для обращения к нему или они могут меняться, в таком случае можно использовать тот же метод FindElements через PowerShell и искать по имени тэга (TagName):
Пример поиска элементов по id и TagТ.
При таком поиске мы можем получить несколько элементов (практически всегда их несколько), т.к. имя тэга не уникально и понадобится дополнительная фильтрация, тут нам могут помочь упомянутые выше свойства объектов ComputedAccessibleLabel и ComputedAccessibleRole, а также Text. Такой подход может занимать продолжительное время при большом количестве элементов. В нашем случае все просто, мы уже знаем нужный нам элемент, его роль combobox:
$Selenium.FindElements([OpenQA.Selenium.By]::TagName('textarea')) | Where-Object ComputedAccessibleRole -eq comboboxЕще мы знаем, что значение Lable не является пустым, и даже если бы мы этого не знали, элемента всего два, не сложно догадаться, который из них нам нужен (или просто предварительно проверить оба).
Есть множество популярных тегов, которые используются для работы с CSS и DOM (Document Object Model), и в нашем случае могут быть полезны для поиска элементов. Такие тэги и их описание не сложно найти в интернете, перечислю некоторые из них, которые использовал в данной статье:
textarea – используется для создания многострочного поля ввода текста;
input – используется для добавления различных элементов ввода, таких как текстовые поля, флажки, радиокнопки и т.п.;
div – используется для создания блочного контейнера на веб-странице, который позволяет группировать другие элементы;
span – также используется для группировки элементов, но в отличие от
divобычно не создает перенос строки и отображается в строке;button – используется для создания кнопок.
Как только мы нашли элемент, помещаем его в переменную, (в нашем случае $Search), чтобы взаимодействовать с ним на прямую. Для передачи текста, используем метод SendKeys:
$Search.SendKeys("calculator online")Теперь, чтобы не повторять поиск элемента с кнопкой поиска, просто используем привычный многим в браузере метод Enter (мы уже выяснили, что данный элемент не поддерживает несколько строк: rows=1):
$Search.SendKeys([OpenQA.Selenium.Keys]::Enter)На данном этапе мы выполнили переход на новый url, где получаем содержимое страницы с результатом поиска. В нашем случае, калькулятор уже находится на странице, специально для примера используем второй подход, где вначале находим все элементы с тэгом div, которые отвечают за нажатие, фильтруем вывод по роли (ComputedAccessibleRole) – Button и лейблу (ComputedAccessibleLabel, также можно использовать свойство Text) – 2. После чего производим два нажатия, используя метод Click(). Выглядит это так:
Start-Sleep 1
$div = $Selenium.FindElements([OpenQA.Selenium.By]::TagName("div"))
$2 = $div | Where-Object {($_.ComputedAccessibleRole -like "button") -and ($_.ComputedAccessibleLabel -like "2")}
$2.Click()
$2.Click()Мы нашли цифру 2, и нажали на нее два раза. Далее находим элемент, отвечающий за сложение. У данного элемента свойство Lable имеет содержимое «сложение», которое отличается от Text, где содержимым является «+», после чего нажимаем на искомый элемент:
$plus = $div | Where-Object {($_.ComputedAccessibleRole -eq "button") -and ($_.Text -eq "+")}
$plus.Click()К остальным элементам обратимся по jsname (которые мы предварительно находим через DevTools). Теперь, нам остается забрать полученный результат, чтобы вывести его в консоль. Для этого достаточно обратиться к нужному элементу и получить значение его свойства Text.
$3 = $Selenium.FindElement([OpenQA.Selenium.By]::CssSelector('[jsname="KN1kY"]'))
$3.Click()
$3.Click()
$sum = $Selenium.FindElement([OpenQA.Selenium.By]::CssSelector('[jsname="Pt8tGc"]'))
$sum.Click()
$result = $Selenium.FindElement([OpenQA.Selenium.By]::CssSelector('[jsname="VssY5c"]')).Text
Write-Host "Result: $result" -ForegroundColor GreenОбратите внимание, как быстро происходит обращение к элементам на прямую. При фильтрации имеющихся 1092 элементов (в примере) с тэгом div, для поиска одного элемента уходит порядка 3-4 секунд, что ощутимо дольше и нецелесообразно для использования в конечном скрипте:
Сравнение скорости при поиске элемента по тэгу и селектору. Получаем наш результат сложения.
Пример целиком опубликован на GitHub.
Для второго случая продемонстрирую пример с ChatGPT. Это альтернатива, т.к. для подобных сервисов (обобщаю в примере с другими модулями) присутствует API, но иногда невозможно получить такой токен бесплатно, а ввиду ограничений в нашей стране даже приобрести напрямую за деньги, что послужило для меня отправной точкой создания подобных модулей. Цель простая, общаться с ИИ используя только консоль PowerShell. Еще, это может оказаться полезным, если вы хотите внедрить такое решение в свой скрипт для генерации ответов (например, произвести парсинг динамических данных) или даже части кода (зависит от задач и потребностей, тенденция растет с каждым днем). И тут действительно все очень просто, нам нужен любой бесплатный сервис, который желательно не требует авторизации, после чего найти элемент, который отвечает за ввод текста и передать в него параметр $Text с нашим запросом (он и будет использоваться параметром в модуле для задаваемых нами вопросов), после чего дождаться ответа и получить результат. Вот пример для вышеупомянутого шаблона:
$Selenium.Navigate().GoToUrl("$url")
$Search = $Selenium.FindElements([OpenQA.Selenium.By]::TagName('textarea')) | Where-Object ComputedAccessibleRole -eq textbox
$Search.SendKeys("$Text")
$Search.SendKeys([OpenQA.Selenium.Keys]::Enter)
while ($true) { if ($($Selenium.FindElements([OpenQA.Selenium.By]::TagName('button')).Text) -eq "Clear") { $Result = $Selenium.FindElements([OpenQA.Selenium.By]::TagName('span')) return $($Result[-2].Text) break }
}Важным моментом является получение конечного результата ответа на наш вопрос, т.к. ChatGPT не отвечает сразу, а постепенно генерирует текст на экране. Нам всегда надо за что-то зацепиться, для проверки, что ответ был получен полностью, только после этого прочитать его. В примере я использую содержимое свойства кнопки – Text, когда оно имеет значение Clear, можно производить новый запрос, соответственно ответ на предыдущий запрос уже был получен. Но это всегда индивидуально, в нашем случае можно было считать кол-во элементов c тэгом div, его значение вырастает на 7 после каждого полученного ответа. Пример работы с таким модулем выглядит так:
Общаемся с ChatGPT в консоли PowerShell.
Если вы уже установили зависимости, то для установки модуля Get-GPT из репозитория GitHub можете воспользоваться следующей командой:
Invoke-RestMethod https://raw.githubusercontent.com/Lifailon/Selenium-Modules/rsa/Modules/Get-GPT/Get-GPT.psm1 | Out-File -FilePath "$(New-Item -Path "$($($Env:PSModulePath -split ";")[0])\Get-GPT" -ItemType Directory -Force)\Get-GPT.psm1" -ForceПо тому же принципу можно написать модуль, который позволят производить запросы для перевода текста. К слову, я уже писал автоматизированный модуль для перевода текста в консоли с использованием бесплатного публичного API для Google Translate и эмулятора DeepLX, для меня это самый удобный способ, который я использую ежедневно. Планирую написать модуль для bash и отдельную статью, т.к. на мой взгляд такой простое решение может оказаться полезно и другим.
Приведу еще один пример, у меня была задача получать значения измерения скорости интернета от провайдера, используя публичные сервисы, такие как LibreSpeed, OpenSpeedTest и Ookla SpeedTest. Во всех случаях достаточно перейти на нужный url сервиса, нажать кнопку измерения, по завершению получить результат – метрики измерений, и заполнить ими объект System.Collections.Generic.List.
Измерение скорости интернета в консоли PowerShell.
В большинстве случаев, когда мы хотим получить результат работы, заранее необходимо найти элемент, в котором эти данные будут нам доступны и зафиксировать его в переменной. В дальнейшем, по результатам проделанных нами действий, мы добавляем в бесконечный цикл найденный ранее элемент и сравниваем его значение с текущим значение этого же элемента, тем самым убеждаясь, что данные обновлены, после чего забираем результат и завершаем цикл. Тот же принцип, если мы ожидаем, что по результату текущий url обновится (т.е. мы перейдем на другую страницу), в таком случае проверяем его. Вот пример:
$Url = "https://www.speedtest.net/"
$Selenium.Navigate().GoToUrl($Url)
Start-Sleep 1
$UrlTemp = $Selenium.Url
$span = $Selenium.FindElements([OpenQA.Selenium.By]::TagName("span"))
$Button = $span | Where-Object Text -Match "GO"
$Button.Click()
while ($True) { if ($Selenium.Url -ne $UrlTemp) { $UrlResult = $Selenium.Url break }
}
$Cont = Invoke-RestMethod $UrlResultВ примере с SpeedTest, если из полученного url нам нужно получить данные всей страницы, то можем просто прочитать его содержимое, используя Invoke-RestMethod (аналог клиента Curl в Windows). Нужные нам значения можно получить по средствам парсинга HTML-страницы, в некоторых случаях можно попытаться вытащить кусок JSON, который PowerShell сразу преобразует в объект и его достаточно только отфильтровать или пересобрать, что очень сильно упрощает процесс:
$Data = ($Cont -split "window.OOKLA.")[3] -replace "(.+ = )|(;)" | ConvertFrom-Json
return $Data.resultИспользуя модуль Get-SpeedTest, можно прямо в консоли получать результаты измерений, которые в свою очередь возможно настроить на отправку в базу данных временных рядов, например, InfluxDB и отображать в виде графиков в Grafana:
Перевод метрик в МБ или ГБ можно средствами самой Grafana, в примере этого не было сделано.
Работу (Ookla-SpeedTest-API) с отправкой метрик измерений скорости интернета я делал еще в Internet Explorer c использованием COM (Component Object Model) интерфейса. Это устаревший вариант, который можно использовать как альтернативу Selenium. Принцип работы у него точно такой же, при этом никаких зависимостей не требуется и работает по сей день. Тем не менее, если станет интересно, я оставлял заметки на тему работы с IE, а также с InfluxDB. Планирую (как только подготовлю тестовый стенд), написать отдельную статью по работе с InfluxDB, используя PowerShell или Bash и настройки графиков в Grafana.
По итогу, если взять за основу представленный выше шаблон и ознакомиться с примерами, можно автоматизировать выполнение процессов в Web-приложениях и даже добавлять такие решения в свои Pipeline.
running powershell commands that need elevated privileges from java
- Create a PowerShell script: This script will perform a task that requires administrative privileges.
- Create a batch script: This batch script will invoke PowerShell with elevated privileges.
- Java code: This code will run the batch script.
Step 1: Create a PowerShell Script
# example.ps1
New-Item -Path "C:\Windows\System32\testfile.txt" -ItemType "File" -ForceStep 2: Create a Batch Script
@echo off
setlocal
set scriptPath=%~dp0example.ps1
# Check if the script is running with administrative privileges
openfiles >nul 2>&1
if '%errorlevel%' == '0' ( # If already running with admin privileges, run the PowerShell script powershell -ExecutionPolicy Bypass -File "%scriptPath%"
) else ( # If not running with admin privileges, relaunch the batch file with elevated privileges echo Requesting administrative privileges... powershell -Command "Start-Process cmd -ArgumentList '/c %~s0' -Verb RunAs"
)
endlocalStep 3: Java Code to Run the Batch Script
Here’s the Java code to run the batch script:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class ProcessLauncher { public static void main(String[] args) { ProcessLauncher launcher = new ProcessLauncher(); String scriptPath = "run_as_admin.bat"; // Adjust the path to your batch script ProcessResult result = launcher.runScript(scriptPath); if (result.getExitCode() == 0) { System.out.println("Script succeeded. Output:"); } else { System.out.println("Script failed with exit code: " + result.getExitCode()); } System.out.println(result.getOutput()); } public ProcessResult runScript(String scriptPath) { ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/c", scriptPath); processBuilder.redirectErrorStream(true); StringBuilder output = new StringBuilder(); try { Process process = processBuilder.start(); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; while ((line = reader.readLine()) != null) { output.append(line).append("\n"); } int exitCode = process.waitFor(); return new ProcessResult(exitCode, output.toString()); } catch (IOException | InterruptedException e) { e.printStackTrace(); return new ProcessResult(-1, e.getMessage()); } }
}
class ProcessResult { private int exitCode; private String output; public ProcessResult(int exitCode, String output) { this.exitCode = exitCode; this.output = output; } public int getExitCode() { return exitCode; } public String getOutput() { return output; } @Override public String toString() { return "ProcessResult{" + "exitCode=" + exitCode + ", output='" + output + '\'' + '}'; }
}Explanation
- PowerShell Script (
example.ps1): Contains the PowerShell command that requires elevated privileges. - Batch Script (
run_as_admin.bat):- Checks if the script is running with administrative privileges using the
openfilescommand. - If already running with administrative privileges, it runs the PowerShell script.
- If not, it relaunches the batch script with elevated privileges using
Start-Processwith theRunAsverb.
- Checks if the script is running with administrative privileges using the
- Java Code:
- Uses
ProcessBuilderto run the batch script. - Captures the output of the batch script.
- Waits for the process to complete and retrieves the exit code.
- Uses
Jun 26, 2024, 10:49 PM

Java является одним из самых популярных языков программирования в мире информационных технологий. Этот универсальный язык используется для разработки приложений, работающих на самых разнообразных платформах, включая серверы Windows. Установка Java на операционной системе Windows Server может потребоваться для запуска различных приложений, веб-серверов, баз данных и многих других задач. В этой статье мы рассмотрим процесс установки Java на сервере, работающем под управлением Windows Server 2016.
Введение
Java — это объектно-ориентированный язык программирования, который стал всеобъемлющим языком и привлек внимание крупных организаций и компаний. Изобретателем языка Java является Джеймс Гослинг. Он пытался изобрести новый язык, который мог бы заменить язык программирования C++ и обладал бы большими возможностями. Язык программирования Java состоит из двух частей: интерфейсов прикладного программирования (API) и виртуальной машины Java (JVM).
Java остается одним из важнейших языков программирования, имеет огромное сообщество разработчиков и богатый экосистемный стек, что делает его мощным инструментом для создания разнообразных приложений и решений.
Основные характеристики Java это:
- Портативность — Java разработан с учетом идеи «Write Once, Run Anywhere» (Пиши один раз, запускай где угодно). Это означает, что программы, написанные на Java, могут выполняться на разных операционных системах без необходимости переписывать код. Это достигается благодаря использованию виртуальной машины Java (JVM), которая интерпретирует байт-код Java и адаптирует его для конкретной платформы.
- Безопасность — Java предоставляет множество механизмов безопасности, которые помогают защитить приложения от вредоносных атак и ошибок в коде.
- Многозадачность — Java поддерживает многозадачность, что означает, что вы можете создавать многопоточные приложения, которые выполняют несколько задач одновременно.
- Обширная библиотека — Java поставляется с богатой библиотекой классов и API, которые облегчают разработку различных видов приложений, включая веб-приложения, мобильные приложения, настольные приложения и многое другое.
- Общее использование — Java широко применяется в различных областях, включая веб-разработку, мобильное программирование (с использованием Android), корпоративные приложения, игровую индустрию и многое другое.
Установка JRE
JRE представляет собой окружение, необходимое для выполнения Java-приложений. Оно включает в себя виртуальную машину Java (JVM), классы библиотеки Java Standard Library и другие компоненты, необходимые для запуска Java-приложений. JRE устанавливается на компьютерах конечных пользователей и серверах, которые должны выполнять Java-приложения, но не разрабатывать их. Пользователи, которым нужно просто запустить Java-приложение, могут использовать JRE для этой цели.
Для установки JRE откройте на вашем VDS удобный для вас брайзер и перейдите на страницу загрузки Java для Windows Server 2016. Здесь выберите для загрузки offline-дистрибутив для 64-х разрядной версии Windows.

После окончания загрузки запустите установщик, где для начала инсталляции, нажмите Install.

Когда установка завершится, нажмите Close для того, закончить работу мастера.

Чтобы проверить версию установленного пакета, запустите командную оболочку PowerShell, для чего в строке поиска наберите powershell.

В командной строке оболочки наберите:

Настройка переменных окружения
Для того, чтобы оболочка Java была доступна для всех приложений и команд на сервере, необходимо добавить соответствующие переменные окружения. Это позволит обеспечить правильное выполнение Java-приложений и упростит управление версиями Java.
Чтобы произвести данную настройку, откройте Панель управления, для чего в строке поиска наберите control.


Здесь в разделе Системные переменные двойным кликом мыши откройте переменную Path.

Теперь при помощи кнопки Создать добавьте значение переменной Path, содержащее путь к каталогу bin, который находится в папке установленного пакета JRE.

Для сохранения изменений нажмите ОК.

Затем с помощью кнопки Создать в разделе Системные переменные создайте новую переменную, имя которой будет JAVA_HOME, а её значение будет содержать путь к папке, в которую установлен JRE.

Для сохранения созданной переменной так же нажмите ОК.

Чтобы все внесённые изменения в переменные окружения были сохранены, закройте окно при помощи кнопки ОК.

This is a quick tip, in order to configure the Windows Powershell for set the Java and Maven variables when is there another Java installation on the system, but you only want to apply on the current terminal session to avoid change the system configuration.
There are two ways to do that:
1. Replacing current environment variables in session.
$env:JAVA_HOME = "C:\Program Files\Java\jdk-11" $env:PATH = "$env:JAVA_HOME\bin;" + $env:PATH $env:M2_HOME = "C:\Program Files\maven\apache-maven-3.5.3" $env:PATH = "$env:M2_HOME\bin;" + $env:PATH
> java -versionopenjdk version "11" 2018-09-25 OpenJDK Runtime Environment 18.9 (build 11+28) OpenJDK 64-Bit Server VM 18.9 (build 11+28, mixed mode)> mvn.cmd -versionApache Maven 3.5.3 (3383c37e1f9e9b3bc3df5050c29c8aff9f295297; 2018-02-24T13:49:05-06:00) Maven home: C:\Program Files\maven\apache-maven-3.5.3\bin.. Java version: 11, vendor: Oracle Corporation Java home: C:\Program Files\Java\jdk-11 Default locale: es_419, platform encoding: Cp1252 OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
2. Replacing current environment variables and execute with the specific Java home
$env:JAVA_HOME = "C:\Program Files\Java\jdk-11" $M2_HOME = "C:\Program Files\maven\apache-maven-3.5.3" $env:Path += ";"+$M2_HOME+"\bin" $env:Path += ";"+$JAVA_HOME+"\bin"
> java -versionjava version "1.8.0_321" Java(TM) SE Runtime Environment (build 1.8.0_321-b07) Java HotSpot(TM) 64-Bit Server VM (build 25.321-b07, mixed mode)> & $env:JAVA_HOME\bin\java.exe -versionopenjdk version "11" 2018-09-25 OpenJDK Runtime Environment 18.9 (build 11+28) OpenJDK 64-Bit Server VM 18.9 (build 11+28, mixed mode)> mvn.cmd -versionApache Maven 3.5.3 (3383c37e1f9e9b3bc3df5050c29c8aff9f295297; 2018-02-24T13:49:05-06:00) Maven home: C:\Program Files\maven\apache-maven-3.5.3\bin.. Java version: 11, vendor: Oracle Corporation Java home: C:\Program Files\Java\jdk-11 Default locale: es_419, platform encoding: Cp1252 OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
This last is very useful when you have installed different Java versions and you don’t need to change your system configuration.
In order to reset the configuration restart the terminal session.
That’s all, bye
In this article, I will show you how to install multiple Java versions on Windows and how to change the Java version on the command line and in PowerShell:

Installing Multiple Java Versions
Installing multiple Java versions in parallel is incredibly easy in Windows. You can download and run the installer for each version, which automatically installs the versions in separate directories.
Download Sources
- Java SE 1.1 – You can no longer install this version on 64-bit Windows.
- Java SE 1.2 – Installed to
C:\jdk1.2.2\andC:\Program Files (x86)\JavaSoft\JRE\1.2\by default – I recommend changing this toC:\Program Files (x86)\Java\jdk1.2.2\andC:\Program Files (x86)\Java\jre1.2.2\for the sake of clarity. - Java SE 1.3 – Installed to
C:\jdk1.3.1_28\by default – I recommend changing this toC:\Program Files (x86)\Java\jdk1.3.1_28\. - Java SE 1.4 – Installed to
C:\j2sdk1.4.2_19\by default – I recommend changing this toC:\Program Files (x86)\Java\jdk1.4.2_19\.
- Java SE 5
- Java SE 6
- Java SE 7
- Java SE 8
- Java SE 9 / OpenJDK 9
- Java SE 10 / OpenJDK 10 (→ The most important new features in Java 10)
- Java SE 11 / OpenJDK 11 (→ The most important new features in Java 11)
- Java SE 12 / OpenJDK 12 (→ The most important new features in Java 12)
- Java SE 13 / OpenJDK 13 (→ The most important new features in Java 13)
- Java SE 14 / OpenJDK 14 (→ The most important new features in Java 14)
- Java SE 15 / OpenJDK 15 (→ The most important new features in Java 15)
- Java SE 16 / OpenJDK 16 (→ The most important new features in Java 16)
- Java SE 17 / OpenJDK 17 (→ The most important new features in Java 17)
- Java SE 18 / OpenJDK 18 (→ The most important new features in Java 18)
- Java SE 19 / OpenJDK 19 (→ The most important new features in Java 19)
- Java SE 20 / OpenJDK 20 (→ The most important new features in Java 20)
- Java SE 21 / OpenJDK 21 (→ The most important new features in Java 21)
- Java SE 22 / OpenJDK 22 (→ The most important new features in Java 22)
- JDK 23 Early-Access Build (→ The most important new features in Java 23)
Define Java Environment Variables
JAVA_HOME– many start scripts use this variable.Path– is used when running a Java binary (such asjavaandjavac) from the console.
These variables should always point to the same Java installation to avoid inconsistencies. Some programs, such as Eclipse, define the Java version in a separate configuration file (for Eclipse, for example, this is the entry “-vm” in the eclipse.ini file).
Manually Setting the Java Environment Variables
The Java installers create various environment variables, which you need to clean up first (see below). The fastest way to change the environment variables is to press the Windows key and type “env” – Windows then offers “Edit the system environment variables” as a search result:

At this point, you can press “Enter” to open the system properties:


- The top list (“User variables”) should not contain any Java-related entries.
- The lower list (“System variables”) should contain an entry “JAVA_HOME = C:\Program Files\Java\jdk-22”. If this entry does not exist, you can add it with “New…”. If it exists but points to another directory, you can change it with “Edit…”.
- Delete the following entries under “Path” (if they exist):
C:\ProgramData\Oracle\Java\javapathC:\Program Files (x86)\Common Files\Oracle\Java\javapath
- Insert the following entry instead:
%JAVA_HOME%\bin

The last entry ensures that Path and JAVA_HOME are automatically consistent.
Attention: this only works for the default setting configured here. If you change JAVA_HOME via the command line, you have to adjust Path accordingly. But don’t worry – the scripts you can download in the next step will do that automatically.
How to Check Your Java Version on Windows
Here’s what you should see:

Install the Scripts to Change the Java Version
To change the Java version on the command line, I have prepared some batch files that you can copy to your system. Here is the link: scripts-up-to-java23.zip
The ZIP file contains scripts named
java22.bat,java21.bat,java20.bat, etc., for all Java versions,- the corresponding files
java20.ps1,java19.ps1, etc. for PowerShell, - plus two common scripts
javaX.batandjavaX.ps1.
I suggest you unpack the scripts to C:\Program Files\Java\scripts.
The scripts look like this:
In the files javaX.bat and javaX.ps1, you probably have to adjust some paths to the installed Java versions.
The scripts update the JAVA_HOME environment variable and insert the bin directory at the beginning of the Path variable. That makes it the first directory to be searched for the corresponding executable when you run Java commands such as java or javac.
(The Path variable gets longer with each change. Do not worry about it. This only affects the currently opened command line.)
Add the Script Directory to the Path
To be able to call the scripts from anywhere, you have to add the directory to the “Path” environment variable (just like you did with “%JAVA_HOME%\bin” in the second step):


If one of the commands does not activate the expected Java version, please check if the path in the javaX.bat and javaX.ps1 files corresponds to the installation path of the Java version you want to activate.
Temporary and Permanent Java Version Changes
The commands presented up to this point only affect the currently opened command line or PowerShell. As soon as you open another command line, the default version defined in step 2 is active again (Java 22, if you have not changed anything).
If you want to change the Java version permanently, just add the parameter “perm” to the corresponding command, e.g.
What You Should Do Next…
Now I would like to hear from you:
Either way, let me know by leaving a comment below.
but the problem is when if one these parameter is empty, one parameter takes the value of another one
You’re likely seeing a long-standing bug present in Windows PowerShell and in PowerShell (Core) up to version 7.2.x – it has been fixed in v7.3+, with selective exceptions on Windows – see this answer for details (it focuses on arguments with embedded double-quotes, but the issue at hand is part of the problem).
Trying to pass an empty string (
"",'') as an argument to an external program is quietly ignored; that is, no argument is passed at all.Note that, by contrast, passing
$nullis ignored by design.
# Pass an empty-string *literal*
python -c 'import sys; print(sys.argv[1:])' '' hi
# Pass an empty string via a *variable*
$someArg=''
python -c 'import sys; print(sys.argv[1:])' $someArg hi- The expected output is
['', 'hi'] - With the bug present, the output is
['hi'], indicating that the empty string wasn’t passed at all.
Passing an empty string literally:
Use
'""'(sic) (`"`"works too):# In WinPS and PS Core v7.2.x- only # (In v7.3+, use it only with $PSNativeCommandArgumentPassing = 'Legacy'.) python -c 'import sys; print(sys.argv[1:])' '""' hiAlternatively, on Windows only, use
--%, the stop-parsing token in combination with""(not''), but note its many limitations, detailed in the bottom section of this answer, notably the inability to embed PowerShell variables and expressions:# Windows only. python -c 'import sys; print(sys.argv[1:])' --% "" hi
Passing an empty string via variables that may or may not contain the empty string:
With an individual argument:
$someArg = '' python -c 'import sys; print(sys.argv[1:])' ($someArg, '""')[$someArg -is [string] -and -not $someArg] hiWith an array of arguments:
$someArgs = '', 'hi' python -c 'import sys; print(sys.argv[1:])' $someArgs.ForEach({ ($_, '""')[$_ -is [string] -and -not $_] })It is the concise equivalent of:
if ($someArg -is [string] -and -not $someArg) { '""' } else { $someArg }In PowerShell (Core) 7+, you can also use the ternary conditional operator
($someArg -is [string] -and -not $someArg ? '""' : $someArg)($someArg, '""')[-not $someArg] # PS v7+ only (-not $someArg ? '""' : $someArg)All variants rely on PowerShell’s implicit to-Boolean conversions, which treat an empty string as
$false, and any nonempty one as$true. The complete rules are summarized in the bottom section of this answer.
If you need to write cross-edition, cross-version PowerShell code, the Native module (Install-Module Native; authored by me), has an ie function (short for: Invoke Executable), which is a polyfill that provides workaround-free, cross-edition (v3+), cross-platform, cross-version, forward-compatible behavior in the vast majority of cases, both for passing empty-string arguments and arguments with embedded " chars. correctly – simply prepend ie to your external-program calls:
# With the 'Native' module installed, simply prepending `ie`
# to your external-program calls ensures that both empty-string
# arguments and arguments with embedded " chars. are passed correctly.
ie python -c 'import sys; print(sys.argv[1:])' '' hi



