У меня есть скрипт 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 >&fd
command
при этом его стандартный вывод перенаправляется в указанный файловый дескриптор, который должен быть установлен ранее.
Более сложная версия, которая должна работать на любой системе *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
стандартный вывод, просто переставив команды.