`Xargs -t` выводит stderr или stdout, и можете ли вы им управлять? - PullRequest
1 голос
/ 09 мая 2009

скажем, у меня есть каталог с hi.txt и blah.txt, и я выполняю следующую команду в командной строке linux-ish

ls *.* | xargs -t -i{} echo {}

вывод, который вы увидите

echo blah.txt
blah.txt
echo hi.txt
hi.txt

я бы хотел перенаправить вывод stderr (скажем, «echo blah.txt» завершился неудачей ...), оставив только вывод команды xargs -t , записанный в std out, но он выглядит как будто это тоже stderr.

ls *.* | xargs -t -i{} echo {} 2> /dev/null

Есть ли способ управлять им, чтобы он выводился на стандартный вывод?

Ответы [ 5 ]

3 голосов
/ 09 мая 2009

Использование:

ls | xargs -t -i{} echo {} 2>&1 >/dev/null

2>&1 отправляет стандартную ошибку от xargs туда, куда идет стандартный вывод; >/dev/null отправляет исходный стандартный вывод на /dev/null. Таким образом, в результате получается, что стандартный вывод содержит команды echo, а /dev/null содержит имена файлов. Мы можем спорить о пробелах в именах файлов и о том, будет ли проще использовать скрипт sed, чтобы поставить 'echo' в начале каждой строки (без опции -t), или использовать ли вы:

ls | xargs -i{} echo echo {}

(Проверено: Solaris 10, Korn Shell; должен работать на других оболочках и платформах Unix.)


Если вы не возражаете увидеть внутреннюю работу команд, мне удалось отделить вывод ошибок от xargs и вывод ошибок выполненной команды.

al * zzz | xargs -t 2>/tmp/xargs.stderr -i{} ksh -c "ls -dl {} 2>&1"

(нестандартная) команда al перечисляет свои аргументы по одному в строке:

for arg in "$@"; do echo "$arg"; done

Первое перенаправление (2>/tmp/xargs.stderr) отправляет вывод ошибки из xargs в файл /tmp/xargs.stderr. Выполнена команда 'ksh -c "ls -dl {} 2>&1"', которая использует оболочку Korn для запуска ls -ld для имени файла с любой ошибкой, выводимой на стандартный вывод.

Вывод в /tmp/xargs.stderr выглядит следующим образом:

ksh -c ls -dl x1 2>&1
ksh -c ls -dl x2 2>&1
ksh -c ls -dl xxx 2>&1
ksh -c ls -dl zzz 2>&1

Я использовал ls -ld вместо echo, чтобы убедиться, что я тестировал ошибки - файлы x1, x2 и xxx существуют, но zzz нет.

Вывод на стандартный вывод выглядел так:

-rw-r--r--   1 jleffler rd          1020 May  9 13:05 x1
-rw-r--r--   1 jleffler rd          1069 May  9 13:07 x2
-rw-r--r--   1 jleffler rd            87 May  9 20:42 xxx
zzz: No such file or directory

При запуске без команды, заключенной в 'ksh -c "..."', перенаправление ввода-вывода было передано команде в качестве аргумента ('ls -ld'), и поэтому сообщалось, что не удалось найти файл '2>&1». То есть xargs сама не использовала оболочку для перенаправления ввода / вывода.

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

Другой довольно очевидный вариант - использовать xargs для написания сценария оболочки, а затем выполнить его оболочкой. Это вариант, который я показал раньше:

ls | xargs -i{} echo echo {} >/tmp/new.script

Затем вы можете увидеть команды с:

cat /tmp/new.script

Вы можете запустить команды для удаления ошибок с помощью:

sh /tmp/new.script 2>/dev/null

И, если вы не хотите видеть стандартный вывод команд, добавьте 1>&2 в конец команды.

2 голосов
/ 09 мая 2009

Так что я верю, что вы хотите иметь стандартный вывод

  • стандартный вывод утилиты, которую xargs выполняет
  • список команд, сгенерированных xargs -t

Вы хотите игнорировать поток stderr, сгенерированный выполненная утилита.

Пожалуйста, поправьте меня, если я ошибаюсь.

Сначала давайте создадим лучшую утилиту для тестирования:

% cat myecho
#!/bin/sh
echo STDOUT $@
echo STDERR $@ 1>&2
% chmod +x myecho
% ./myecho hello world
STDOUT hello world
STDERR hello world
% ./myecho hello world >/dev/null
STDERR hello world
% ./myecho hello world 2>/dev/null
STDOUT hello world
%

Итак, теперь у нас есть что-то, что на самом деле выводит как в stdout, так и в stderr, поэтому мы могу быть уверен, что мы получаем только то, что хотим.

Тангенциальный способ сделать это - не использовать xargs, а make. Повторение команды а потом делать это вроде того, что делает make. Это его сумка.

% cat Makefile
all: $(shell ls *.*)

$(shell ls): .FORCE
  ./myecho $@ 2>/dev/null

.FORCE:
% make
./myecho blah.txt 2>/dev/null
STDOUT blah.txt
./myecho hi.txt 2>/dev/null
STDOUT hi.txt
% make >/dev/null
% 

Если вы привязаны к использованию xargs, вам нужно изменить свою утилиту так, чтобы Xargs использует так, что он превосходит stderr. Тогда вы можете использовать 2>&1 трюк с другими упомянул о перемещении списка команд, сгенерированного xargs -t, из stderr в стандартный вывод.

% cat myecho2
#!/bin/sh
./myecho $@ 2>/dev/null
% chmod +x myecho2
% ./myecho2 hello world
STDOUT hello world
% ls *.* | xargs -t -i{} ./myecho2 {} 2>&1
./myecho blah.txt 2>/dev/null
STDOUT blah.txt
./myecho hi.txt 2>/dev/null
STDOUT hi.txt
% ls *.* | xargs -t -i{} ./myecho2 {} 2>&1 | tee >/dev/null
%

Таким образом, этот подход работает и сворачивает все, что вы хотите вывести (исключая то, что вы не хотите).

Если вы часто этим занимаетесь, вы можете написать общую утилиту для отключения stderr:

% cat surpress_stderr
#!/bin/sh
$@ 2>/dev/null
% ./surpress_stderr ./myecho hello world
STDOUT hello world
% ls *.* | xargs -t -i{} ./surpress_stderr ./myecho {} 2>&1
./surpress_stderr ./myecho blah.txt 2>/dev/null
STDOUT blah.txt
./surpress_stderr ./myecho hi.txt 2>/dev/null
STDOUT hi.txt
%
1 голос
/ 09 мая 2009

xargs -t выводит команды, которые должны быть выполнены в stderr перед их выполнением. Если вы хотите, чтобы они вместо этого передавали stderr, вы можете передать stderr в stdout с помощью конструкции 2>&1:

ls *.* | xargs -t -i{} echo {} 2>&1
1 голос
/ 09 мая 2009

Похоже, xargs -t идет в stderr, и с этим ничего не поделаешь.

Вы можете сделать:

ls | xargs -t -i{} echo "Foo: {}" >stderr.txt | tee stderr.txt

для отображения только данных stderr на вашем терминале во время выполнения вашей команды, а затем выполните grep через stderr.txt после, чтобы увидеть, произошло ли что-нибудь непредвиденное, в соответствии с grep -v Foo: stderr.txt

Также обратите внимание, что в Unix ls *.* не так, как вы отображаете все. Если вы хотите увидеть все файлы, просто запустите ls самостоятельно.

0 голосов
/ 10 июня 2010

Как я понимаю, ваша проблема с использованием GNU Parallel http://www.gnu.org/software/parallel/ будет делать правильно:

ls *.* | parallel -v echo {} 2> /dev/null
...