Ошибки в вашей команде связаны с тем, что fd 4 вообще не открыт.
На самом деле вы получаете два сообщения "плохой файловый дескриптор", одно из wc -l
, а другое изcat <&4
(илиxargs -a /dev/fd/4
).
Вам понадобится безымянный канал, чтобы открыть fd 4, но единственный официальный способ получить безымянный канал в Bash — использовать команду coproc
.
Тем не менее, для вашего конкретного варианта использования есть хороший короткий -вариант
Этот трюк не задокументирован, начиная с Bash v5, но работает по крайней мере на v4.3 (пока не удалось протестировать v5 ).
Он использует преимущества нескольких стандартных идиом, которые в совокупности позволяют получать произвольные «безымянные» каналы в системах, которые их поддерживают. Под «безымянный канал » я имею в виду «FIFO, не требующий предварительного создания файла типа p
в файловой системе -с помощью mkfifo
или эквивалентной команды ». (Это определение для безымянного -канала не является правильным, но я осмелюсь сказать, что это то, что действительно имеет смысл при использовании командной оболочки ).
Пример использования этих «безымянных» каналов сводится к следующему:
cat email.txt | ( : {pipe}<> <(:) ; tee >(sed -e '1,/^$/d' | wc -l >&${pipe}) | xargs -I% -a <({ read count ; echo $count; } <&${pipe}) sed -e '1,/^$/{/^Subject:/Is/$/ (%)/}' )
Приведенная выше командная строка должна дать ожидаемый результат в соответствии с вашим примером.
Разбито для объяснения:(только для ясности, не работает при копировании и вставке)
cat email.txt | \ # pipe data to...
( \ # a compound statement, which...
: {pipe}<> <(:) ; \ #... first opens the unnamed pipe in RW mode and put its fd into the (arbitrary) variable ${pipe}
tee \ # then mirrors the data from main stdin to...
>( \ # the side processing of main input...
sed -e '1,/^$/d' | wc -l \ #... which counts the body lines sending the result...
>&${pipe} \ #... to the unnamed pipe
) \
| \ # the tee also pipes all main input to...
xargs -I% -a \ # an xargs that reads iterative lines from...
<({ read count ; echo $count ; } <&${pipe}) \ # a compound command that reads the one-single line (being the count provided by wc) from ${pipe} fd, and echoes it back to xargs -a
sed -e \ # that finally executes the sed command which looks for Subject: line in header part
'/1,^$/{/^Subject:/Is/$/ (%)/}' ; \ # to append it with the count number
)
Несколько дополнительных замечаний:
read
.Если вместо этого вам нужно будет прочитать несколько строк из бокового -канала, вам понадобится какое-то уведомление EOF в полосе -, например, простая строка EOF для добавления в самый конец вывода, а затем отфильтровать во время чтения для xargs -a
и залог -из чтения. Это вполне выполнимо, но довольно долго печатать в командной строке. Избавление от строки EOF в диапазоне -также возможно, но еще более сложно exec {pipe}<&-
; в этом примере мне не нужно было этого делать, потому что fd создается в подпроцессе Для полноты картины ниже приведена эквивалентная версия с использованием coproc
, которая обеспечивает настоящий безымянный канал через обычную пару взаимосвязанных файловых дескрипторов -.
Может быть довольно много подходов к использованию coproc, однако для вашего случая я полагаю, что лучшим будет следующий:
cat email.txt | (coproc cat ; : {input}<&${COPROC[0]} {output}>&${COPROC[1]} ; tee >(sed -e '1,/^$/d' | wc -l >&${output}) | xargs -I% -a <(exec cat <&${input}) sed -e '1,/^$/{/FOO/Is/$/ (%)/}' & )
Разбито для объяснения:(только для ясности, не работает при копировании и вставке)
cat email.txt | \ # pipe data to...
( \ # a subcommand statement, which...
coproc cat ; \ #... first spawns the coprocess, a simple cat command acting as a simple line-oriented bridge
: {cp_output}<&${COPROC[0]} {cp_input}>&${COPROC[1]} ; \ # then copies coproc’s own fds into new ones whose number are put into (arbitrary) variables ${cp_output} and ${cp_input}
tee \ # and then mirrors the data from main stdin to...
>( \ # the side processing of main input...
sed -e '1,/^$/d' | wc -l \ #... which counts the body lines sending the result...
>&${output} \ #... to the (bridging) coproc
) \
| \ # the tee also pipes all main input to...
xargs -I% -a \ # an xargs that reads iterative lines from...
<(exec cat <&${pipe}) \ # another cat that reads from the coproc bridging the count provided by wc, and echoes it back to xargs -a
sed -e \ # that finally executes the sed command which looks for Subject: line in header part
'/1,^$/{/^Subject:/Is/$/ (%)/}' ; \ # to append it with the count number
)
Еще несколько дополнительных примечаний:
exec {cp_input}<&-
илиexec {COPROC[1]}<&-
cat
соединения двух fds является удобным решением общего назначения; однако вы можете оптимизировать производительность, если вам удастся внедрить любой рабочий процесс в сам coproc; в этом примере вам потребуется много переупорядочивать всю командную строку ${COPROC[*]}
не экспортируется в дочерние процессы, а его собственные fds всегда закрываются при выполнении xargs -a
, который активно читает из стандартного ввода и файл, указанный в -a
, таким образом не позволяя tee
заполнять буферы каналов, в противном случае тупик, и вам понадобится более сложный подход, чтобы избежать его Насколько мне известно, в SystemD ничего подобного нет.
Вы можете использовать
sudo
udisksctl unlock
с конфигурацией PolKit для определенных пользователей или групп