Как скрипт bash может записывать свой стандартный вывод из конвейера?

Как скрипт bash может записывать свой стандартный вывод из конвейера?

У меня есть скрипт bash, который вызывает функцию. Функция, помимо прочего, выполняет конвейер, который забирает ее вывод. Чтобы упростить, вот надуманный пример:

#!/bin/bash
func() {
  ls "$@" | sort | rev > /tmp/output
}
func "$@"

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

$ ./myscript .

Теперь, скажем, я хочу вывести sortна стандартный вывод. Как мне это сделать?

Я могу добиться этого следующим образом:

ls "$@" | sort | tee /dev/tty | rev > /tmp/output

Однако использовать /dev/ttyего неправильно, так как это не сработает:

$ ./myscript > myfile

Существует ли более правильный способ обратиться к стандартному выводу скрипта bash из конвейера внутри функции?

(Я использую bash 4.3.0 на Arch Linux)

решение1

Самый простой способ, который я могу придумать, это:

ls "$@" | sort | tee >(rev > /tmp/output)

Отправит teeодну копию в STDOUT, и поскольку после него больше нет |, это наследуется, то есть отправится в TTY, если не перенаправлено, и ваш, myfileесли перенаправлено.
Другая копия будет отправлена rev > /tmp/output​​в его STDIN. В bash >(...)запускает то, что находится в скобках, а затем заменяет на >(...)путь к каналу, подключенному к его STDIN.

решение2

Поскольку другой ответ не дает ясного ответа на этот вопрос, то другой (другой) способ —

exec 3>&1
ls | sort | tee /dev/fd/3 | rev > /tmp/output

Theexec 3>&1 дубликатопределяет файловый дескриптор 1 (stdout) как файловый дескриптор 3. Затем tee /dev/fd/3записывает копию sortвыходных данных в этот файловый дескриптор. Это должно работать в любой оболочке, но может зависеть от операционной системы. Вариант, который должен быть независим от ОС, но специфичен для нее, bashэто:

exec 3>&1
ls | sort | tee >( cat >&3 ) | rev > /tmp/output

В bash, дает вам дескриптор файла для канала к запущенному процессу>(command)command. В любой (?) оболочке запускаетсяcommand >&fdcommand при этом его стандартный вывод перенаправляется в указанный файловый дескриптор, который должен быть установлен ранее.

Более сложная версия, которая должна работать на любой системе *nix (Posix), это

pipe_to_stdout=$(mktemp -u)
mkfifo "$pipe_to_stdout"
cat "$pipe_to_stdout" &
ls | sort | tee "$pipe_to_stdout" | rev > /tmp/output
rm "$pipe_to_stdout"

что практически то же самое, что и во втором примере ( catчтение из fifo и запись в stdout), но с меньшим количеством скрытых данных на уровне оболочки.

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

Связанный контент