Когда процесс завершается, PPID его дочерних процессов устанавливается в 1 (принятие init), но PGID (идентификатор группы процессов) и SID (идентификатор сессии) не изменяются.
Дочерние процессы, вероятно, не меняют свою группу процессов, если только они не предназначены быть демонами. Если это не так, запустите тестируемый процесс в его собственной группе процессов. Вызовите setpgid(getpid(), getpid())
из вашего тестового фреймворка, после форка и перед вызовом execve
для выполнения тестируемой программы. Вызовите kill(-test_program_pid, 0)
(kill
с отрицательным аргументом pid
и значением сигнала 0), чтобы проверить, существует ли запущенный процесс с PGID test_program_pid
. Передайте SIGKILL
в качестве аргумента сигнала, чтобы убить их всех.
test_program_pid = fork();
if (test_program_pid) {
waitpid(test_program_pid, &status, 0);
if (kill(-test_program_pid, 0)) {
record_failue("some child processes were not terminated properly");
}
kill(-test_program_pid, SIGKILL);
} else {
setpgid(getpid(), getpid());
execve("/program/to/test", …);
}
Альтернативным методом может быть создание временного файла и открытие его в программе, которую вы тестируете, и нигде больше. Если программа вызывает execve
, убедитесь, что дескриптор файла открыт без флага O_CLOEXEC
(или вызовите fcntl(fd, FD_CLOEXEC, 0)
). Этот метод предполагает, что программа не идет и не закрывает файловые дескрипторы, которые она явно не использует. Затем вы можете выполнить fuser /temp/file
, чтобы перечислить процессы, у которых открыт этот файл, и fuser -k /temp/file
, чтобы убить их. Вариант этого подхода, который работает даже с программами, закрывающими неиспользуемые дескрипторы файлов, но предполагает, что программа не меняет свой текущий каталог, заключается в создании временного каталога и переходе в этот каталог для запуска программы.
Хотя я не совсем понимаю , зачем вам это , это один из способов сделать это. Ключом является $ ()
для захвата вывода и присвоения его переменной.
Код:
#!/usr/bin/env bash
IFS='' read -r -d '' TEST_STR <<-'EOF'
Test text
quick brown fox
lazy dog
EOF
NEW_LINE_COUNT=$(echo "$TEST_STR" | wc -l)
echo "$TEST_STR"
echo "$NEW_LINE_COUNT"
Результаты:
Test text
quick brown fox
lazy dog
4
Если у вас многострочная строка, вы можете просто использовать wc -l
для подсчета количества строк в нем:
printf '%s' "$string" | wc -l
Чтобы сохранить это число в переменной:
nlines=$( printf '%s' "$string" | wc -l )
Однако вам не нужно это число для выполнения цикла while:
printf '%s' "$string" |
while read -r line; do
# do something with "$line"
done
В зависимости от в зависимости от того, что вы делаете с каждой строкой, вам может вообще не понадобиться цикл while:
printf '%s' "$string" | awk '<some awk script>'
В зависимости от того, как генерируются данные, вам может вообще не понадобиться сохранять их в многострочной переменной:
some_command | some_line_by_line_processing
Здесь some_command
может быть циклом while, набором команд в подоболочке ( (...)
) или простой командой, которая генерирует вывод и some_line_by_line_processing
аналогичным образом может быть простой командой (например, awk
) или циклом или чем-то еще, что вам нужно для обработки вывода первой части конвейера построчно.
Вам не нужно подсчитывать строки в многострочной строке, чтобы просмотреть отдельные устройства. Цикл for
в тандеме с переменными массива можно использовать здесь следующим образом:
IFS=$'\n' read -d '' -r -a Multi_Line_Str <<STR
$(printf '%s\n' 'device_'{A..E})
STR
for device in "${Multi_Line_Str[@]}"
do
echo "<$device>"
done