Необходимо использовать выражение оценки
#!/bin/bash
VER_URXVT='urxvt -help 2>&1 | head -n 2'
echo $VER_URXVT
eval $VER_URXVT
От оценки страницы справочника
The args are read and concatenated together into a single com-
mand. This command is then read and executed by the shell, and
its exit status is returned as the value of eval. If there are
no args, or only null arguments, eval returns 0.
А как насчет чего-то подобного, используя решения из этого SO Q&A под заголовком: Есть ли утилита Unix для подготовки временных меток к строкам текста? и этот SO Q&A под заголовком: труба STDOUT и STDERR к двум разным процессам в сценарии командной строки?.
Шаг 1, мы создаем 2 функции в Bash, которые при вызове:
$ msgOut () { awk '{ print strftime("STDOUT: %Y-%m-%d %H:%M:%S"), $0; fflush(); }'; }
$ msgErr () { awk '{ print strftime("STDERR: %Y-%m-%d %H:%M:%S"), $0; fflush(); }'; }
Шаг 2 вы бы использовали вышеперечисленные функции так, чтобы получить нужное сообщение:
$ { { { ...command/script... } 2>&3; } 2>&3 | msgErr; } 3>&1 1>&2 | msgOut
Здесь я придумал пример, который будет записывать a
в STDOUT, спит 10 секунд, а затем записывает вывод в STDERR. Когда мы помещаем эту последовательность команд в нашу вышеприведенную конструкцию, мы получаем сообщения, как вы указали.
$ { { echo a; sleep 10; echo >&2 b; } 2>&3 | \
msgErr; } 3>&1 1>&2 | msgOut
STDERR: 2014-09-26 09:22:12 a
STDOUT: 2014-09-26 09:22:22 b
Есть инструмент под названием annotate-output
, который является частью пакета devscripts
, который будет делать то, что вы хотите. Единственным ограничением является то, что он должен запускать скрипты за вас.
Если мы поместим вышеприведенную последовательность команд в скрипт под названием mycmds.bash
так:
$ cat mycmds.bash
#!/bin/bash
echo a
sleep 10
echo >&2 b
Затем мы можем запустить его так:
$ annotate-output ./mycmds.bash
09:48:00 I: Started ./mycmds.bash
09:48:00 O: a
09:48:10 E: b
09:48:10 I: Finished with exitcode 0
Формат вывода может управляться для части, содержащей метку времени, но не более того. Но он похож на то, что вы ищете, так что он может соответствовать счету.
Я подозреваю, что часть вашей проблемы связана с тем, как вы построили свой heredoc. Попробуйте это так:
$ ftp -n ${FTP_HOST} << STOP
user ${FTP_USERNAME} ${FTP_PASSWORD}
binary
lcd ${FTP_FROM_DIR}
cd ${FTP_TO_DIR}
put ${reportFileName}
STOP
Если вы действительно хотите, чтобы эти пробелы/вкладки в команде, то вам нужно будет перейти к этой форме heredoc:
$ ftp -n ${FTP_HOST} <<-STOP
user ${FTP_USERNAME} ${FTP_PASSWORD}
binary
lcd ${FTP_FROM_DIR}
cd ${FTP_TO_DIR}
put ${reportFileName}
STOP
Всякий раз, когда у вас есть пробелы/вкладки в heredoc вы должны использовать < < -
, чтобы сказать оболочке, чтобы удалить ведущие пробелы/вкладки перед выполнением команд, которые содержатся в.
Когда вы анализируете переменные в сценариях и невинно используете эхо
для печати сообщений, вы обычно получаете больше, чем просто последовательность, который вам интересен. echo
также включает символ конечной новой строки. Его можно увидеть в следующем примере:
$ echo $(echo hi) | hexdump -C
00000000 68 69 0a |hi.|
00000003
Код 0a
является кодом ASCII для новой строки. Это поведение обычно нужно отключить с помощью переключателя -n
на echo
:
$ echo -n $(echo hi) | hexdump -C
00000000 68 69 |hi|
00000002
или даже лучше, модернизация с помощью printf
.
Можно использовать perl
:
$ perl -e 'print pack "I>", shift' $(( RANDOM << 17 | RANDOM << 2 | RANDOM >> 13 ))
-121--84652- . Простая оболочка, которая подает оба выхода данной команды в два экземпляра sed
(один для stderr
, другой для stdout
), которые выполняют маркирование.
#!/bin/bash
exec 3>&1
coproc SEDo ( sed "s/^/STDOUT: /" >&3 )
exec 4>&2-
coproc SEDe ( sed "s/^/STDERR: /" >&4 )
eval $@ 2>&${SEDe[1]} 1>&${SEDo[1]}
eval exec "${SEDo[1]}>&-"
eval exec "${SEDe[1]}>&-"
Обратите внимание на несколько вещей:
Это волшебное воплощение для многих людей (включая меня) - не просто так (см. связанный ответ ниже).
Нет никакой гарантии, что он не будет иногда менять пару строк - все зависит от планирования сопроцессов. Собственно, почти гарантировано, что в какой-то момент времени так и будет. При строгом сохранении порядка необходимо обработать данные из stderr
и stdin
в одном и том же процессе, в противном случае планировщик ядра может (и будет) создать из этого беспорядок.
Если я правильно понимаю проблему, это означает, что вам нужно поручить оболочке перенаправить оба потока на один процесс (что можно сделать AFAIK). Проблема начинается, когда этот процесс начинает решать, что действовать в первую очередь - он должен будет опросить оба источника данных и в какой-то момент перейти в состояние, когда он будет обрабатывать один поток и данные будут поступать в оба потока до его завершения. И именно там он и разрушается. Это также означает, что свертывание выходных syscalls, таких как stderred
, вероятно, является единственным способом достижения желаемого результата (и даже тогда у вас может возникнуть проблема, когда что-то станет многопоточным в многопроцессорной системе).
Что касается сопроцессов, обязательно прочитайте превосходный ответ Стефана в Как вы используете команду coroc в Bash? для глубокого понимания.
Ввиду того, что ваши сценарии, по сути, разбивают свой вывод на два отдельных канала, становится физически невозможным, чтобы даже одна программа, подключенная к этим каналам, на 100 % гарантировала отсутствие перестановки строк -, ей пришлось бы выполнять чередующиеся не -блокирует чтение из этих каналов, и в какой-то момент ему придется решать, из какого канала читать дальше. Тем не менее, я подозреваю, что даже эмуляторам терминала нельзя доверять в идеальном сохранении порядка строк.