Использование именованного канала для получения кодов выхода замены процесса

Поскольку вы используете SLES, вы можете использовать расширения GNU, чтобы сделать это безопаснее:

find trainB -mindepth 1 -maxdepth 1 ! -name '.*' -print0 |
  shuf -n 5533 -z |
  xargs -r0 mv -t testB

Это использует findдля обработки списков файлов через каналы вместо аргументов строки команды -, затем перемешивает их, ограничивая вывод, и, наконец, перемещает их в testB. Параметры -print0, -zи -0обеспечивают использование нулевых терминаторов вместо символов новой строки.

Вместо findможно использовать:

printf '%s\0' trainB/*

printfвстроен -в bash, на него не влияет слишком длинный список аргументов ограничение системного вызова execve(). Однако это потенциально менее эффективно, так как оболочке необходимо построить весь список и отсортировать его, в то время как findотображает пути к файлам без сортировки по мере их поступления.

1
06.06.2021, 01:10
1 ответ

На самом деле это не кажется слишком простым. Вероятно, было бы лучше, если бы оболочка поддерживала ожидание замены процесса, но я не думаю, что Bash делает это.

Другая проблема заключается в том, что именованный канал не может знать, сколько строк вы собираетесь туда записать. Канал будет считывать EOF, когда все модули записи закрыты, но вы, скорее всего, получите по одному для каждой echoзаписи. Если только они не попали в одно и то же время, в этом случае вы этого не сделаете.

Но представляется возможным организовать подстановки процессов так, чтобы запись fd была открыта с самого начала, чтобы EOF появлялся только один раз после того, как все они завершены.

Что-то вроде этого, где echo somecommand, trueи falseобозначают фактические команды:

#!/bin/bash

dir=$(mktemp -d)
p="$dir/p"
mkfifo "$p"

# whole subshell sent to the background
(exec 3> "$p"; 
 # both process substitutions get a copy of fd 3
 echo somecommand \
    <(false; echo "cmd1: $?" >&3) \
    <(true;  echo "cmd2: $?" >&3) \

) &

# read the exit statuses, this will see EOF once all the three
# background processes above finish
cat "$p"
rm -rf "$dir" 2>/dev/null

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

Кроме того, если echo somecommandработает медленно, вывод из cat "$p"может появиться первым. Вам нужно будет прочитать данные из канала в переменную, а затем waitдля фонового процесса.

Также есть возможность без фоновой обработки somecommand, но для этого нужна дополнительная гимнастика с файловыми дескрипторами:

#!/bin/bash

dir=$(mktemp -d)
p="$dir/p"
mkfifo "$p"

# open an fd for read+write (doesn't block because both open)
exec 3<>"$p"

# process substs inherit the fd, closing it when they exit
echo somecommand \
    <(false; echo "cmd1: $?" >&3) \
    <(true;  echo "cmd2: $?" >&3) \

# open another reader to keep the pipe live
exec 4<"$p"

# now we can close the writing handle
exec 3>&-

# read the data off
cat <&4
exec 4<&-

rm -rf "$dir" 2>/dev/null

Может быть проще просто собрать статусы выхода в обычный файл и читать его, пока не появится известное количество строк.

#!/bin/bash

f=$(mktemp)

# number of process substitutions
n=2
echo somecommand \
    <(false; echo "cmd1: $?" >>"$f") \
    <(true;  echo "cmd2: $?" >>"$f") \

exec 3< "$f"
# read that many lines
for ((i = 0; i < n; i++)) do
    # if the data isn't there yet, retry reading until a new line appears
    until read line <&3; do sleep 1; done
    echo "$line";
done 
exec 3<&-

rm -f "$f"

Насколько я проверял, все три работают, но работа с подстановками процессов и конвейерами может быть достаточно сложной, поэтому я мог упустить какой-то режим отказа.

3
28.07.2021, 11:26

Теги

Похожие вопросы