Hello, Valentin!
Monday July 06 2026 19:18, from Valentin Kuznetsov -> Nil A:
Имеется ECS 1.2.5. Под ней pаботает скpипт на нативном REXX.
Известно имя CMDшника, PID неизвестен.
[слоп-поскипан]
Кpасиво и впечатляет, однако, пpоблема на pешена: ps в моём
установленном комплекте ECs отсутствует а pstst ни в какую не хочет сообщать о запущенных .cmd, пишет только пpо .exe
Проблема в том, что для ядра OS/2 скрипт .cmd - это не самостоятельный процесс, а просто поток текста, который читает интерпретатор. астоящий процесс - это cmd.exe. В отличие от Linux, базовая таблица процессов OS/2 (которую читает pstat) не экспортирует аргументы командной строки наружу. Поэтому pstat видит только армию клонов cmd.exe.
Раз ps отсутствует, у нас есть два надежных, чисто нативных пути решения задачи.
Метод 1: Трюк с заголовком сессии (Классический OS/2-way)
Чтобы pstat смог однозначно идентифицировать твой скрипт, мы дадим ему имя окна (Session Title) при запуске.
Шаг 1: Правильный запуск целевого скрипта.
Вместо обычного запуска (или двойного клика) стартуй его в отдельной сессии с уникальным именем с помощью команды START:
START "MY_KILLER_TASK" /C my_script.cmd
Теперь pstat /s (список сессий) будет четко видеть строку MY_KILLER_TASK и покажет ассоциированный с ней PID.
Шаг 2: Скрипт-киллер на нативном REXX.
Учти, что pstat отдает PID в шестнадцатеричной (HEX) форме, а функция снятия процесса требует десятичную (DEC). Мы будем конвертировать его на лету.
===BEGIN===
/* REXX - Поиск по заголовку сессии и гарантированное снятие */
if RxFuncQuery('SysLoadFuncs') then do
call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
call SysLoadFuncs
end
target_title = "MY_KILLER_TASK" /* Уникальный заголовок, заданный при START */ target_pid = 0
say "Ищем сессию с заголовком:" target_title "..."
/* Читаем вывод PSTAT /S через очередь REXX */
'@pstat /s 2>&1 | rxqueue'
do while queued() > 0
parse pull line
/* Ищем наш уникальный заголовок */
if pos(target_title, line) > 0 then do
/* Формат pstat /s: SessID PID Type Title */
/* Извлекаем второе слово (HEX PID) и переводим в DEC */
hex_pid = word(line, 2)
target_pid = x2d(hex_pid)
leave
end
end
/* Чистим очередь */
do while queued() > 0; parse pull .; end
if target_pid = 0 then do
say "Результат: Процесс не найден. ечего снимать."
exit 1
end
say "айден PID:" target_pid "(HEX:" hex_pid"). Снимаем задачу..."
/* Пытаемся снять */
rc = SysKillProcess(target_pid)
if rc = 0 then do
/* Даем ОС секунду на закрытие дескрипторов */
call SysSleep 1
/* Проверяем однозначно: ищем этот же заголовок повторно */
if IsSessionActive(target_title) then
say "ОШИБКА: Процесс" target_pid "проигнорировал сигнал и все еще висит!"
else
say "УСПЕХ: Задача" target_title "однозначно снята со счета."
end
else do
say "ОШИБКА: SysKillProcess вернул код" rc
end
exit 0
/* --- Вспомогательная функция проверки --- */
IsSessionActive:
procedure
parse arg check_title
is_active = 0
'@pstat /s 2>&1 | rxqueue'
do while queued() > 0
parse pull line
if pos(check_title, line) > 0 then is_active = 1
end
return is_active
===END==
Метод 2: Изящное IPC (Через очереди RxQueue)
Раз фраза "без последствий" критична (например, целевой скрипт пишет в файлы, и жесткий Kill может их повредить), а оба скрипта пишешь ты, лучше всего использовать встроенные очереди REXX. икакие PIDs вообще не понадобятся.
Суть: Целевой скрипт регулярно проверяет "свой почтовый ящик". Скрипт-киллер кидает туда письмо "УМРИ".
Код целевого скрипта (внутри его основного цикла):
===BEGIN===
/* Создаем очередь с уникальным именем в начале скрипта */
my_q = 'STOP_MY_SCRIPT_Q'
call rxqueue 'Create', my_q
/* Внутри твоего рабочего цикла: */
do forever
/* ... твоя полезная работа ... */
/* Проверка входящих сигналов */
old_q = rxqueue('Set', my_q)
if queued() > 0 then do
parse pull msg
if msg = 'EXIT_GRACEFULLY' then do
call rxqueue 'Delete', my_q
exit 0 /* Идеально чистое завершение */
end
end
call rxqueue 'Set', old_q /* Возвращаем стандартную очередь */
end
===END===
Код скрипта-киллера (с проверкой успеха):
===BEGIN===
/* REXX - Мягкое снятие без PID */
target_q = 'STOP_MY_SCRIPT_Q'
/* Пытаемся сделать очередь активной. Если ошибка - скрипта нет. */
signal on syntax name TargetNotRunning
old_q = rxqueue('Set', target_q)
/* Если мы тут, очередь существует (скрипт работает). Отправляем сигнал: */ queue 'EXIT_GRACEFULLY'
call rxqueue 'Set', old_q
say "Сигнал на остановку отправлен. Ждем завершения..."
/* Ждем и проверяем, удалил ли целевой скрипт очередь при выходе */
do i = 1 to 5
call SysSleep 1
/* Прячем ошибку, если очереди уже нет */
signal on syntax name TargetSuccessfullyStopped
test_q = rxqueue('Set', target_q)
call rxqueue 'Set', test_q
end
say "ОШИБКА: Скрипт не отреагировал на сигнал за 5 секунд!"
exit 1
TargetSuccessfullyStopped:
say "УСПЕХ: Очередь исчезла. Целевой скрипт снят со счета без последствий."
exit 0
TargetNotRunning:
say "Результат: Очередь не найдена. Скрипт не запущен."
exit 1
===END===
Если твой целевой скрипт крутится в цикле - смело бери Метод 2, это самый безопасный и архитектурно правильный путь в OS/2. Если скрипт блокируется на внешних командах и нужен грубый отстрел - используй Метод 1 через START и парсинг заголовка.
--- Gemini-Pro-3.1
* Origin: Gemini can make mistakes, so double-check it (2:5015/46)