Почему `paste` не может печатать stdin рядом с stderr?

Этот тип проблемы может быть сложно отладить. Для начала полезно изолировать его от проблем с доступом к сети или жесткому диску.

Это достигается путем устранения потенциальных причин до тех пор, пока вы не останетесь с виновником.

Предпосылки

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

Также мы будем использовать системную консоль. Для доступа к нему выполните следующие действия: Ctrl + Alt + F2 . Вы можете использовать аналогичную комбинацию клавиш, чтобы вернуться к основному дисплею, где находится графический рабочий стол. Это будет следующая комбинация клавиш: Ctrl + Alt + F1 .

Сеть

Использование приложения nethogs - хорошее место для начала. Мне нравится его использовать, поскольку он показывает приложения, которые пытаются получить доступ к сети. Возможно, одно из этих приложений вызывает зависание.

Прежде чем мы сможем использовать nethogs , нам нужно определить, какой сетевой интерфейс используется нашей системой. Вот те, которые у меня есть на моем ноутбуке:

$ ip -o link show | cut -d" " -f2
lo:
em1:
wlp3s0:
virbr0:
virbr0-nic:
vboxnet0:

Я знаю по опыту, что моя беспроводная связь - wplp3s0 . Ethernet - это em1 . Начнем с WiFi.

$ sudo nethogs wlp3s0

Результатом этого типа вывода:

NetHogs version 0.8.0

  PID USER     PROGRAM                      DEV        SENT      RECEIVED       
979   saml     ../bin/google-chrome-stable  wlp3s0     1.943       2.547 KB/sec
2376  saml     /usr/bin/pidgin              wlp3s0     0.000       0.000 KB/sec
21789 saml     ssh                          wlp3s0     0.000       0.000 KB/sec
9618  saml     ssh                          wlp3s0     0.000       0.000 KB/sec
10267 saml     ssh                          wlp3s0     0.000       0.000 KB/sec
?     root     unknown TCP                             0.000       0.000 KB/sec

  TOTAL                                                1.943       2.547 KB/sec 

Как только мы изолировали проблему от нескольких PID, которые кажутся однобокими из-за большого количества данных SENT без получения данных, мы нужно погрузиться глубже и использовать strace , чтобы попытаться увидеть, какой аспект этого сетевого доступа зависает. Для этого вы можете использовать strace следующим образом:

$ strace -s 2000 -o somepid.log -p <PID>

Где - это один из идентификаторов процесса, идентифицированных из nethogs .

Дисковый ввод-вывод

Если мы определили, что проблема не в нашей сети, следующим местом для исследования будет поиск того, есть ли у процесса проблемы с доступом к жесткому диску и блокировка каким-либо образом .

Это может быть сложнее отладить, но вы, вероятно, воспользуетесь такими инструментами, как lsof , strace и fatrace для дальнейшего уточнения поиска.

Что-нибудь еще?

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

ss of dialog

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

3
05.07.2019, 10:51
2 ответа

Почему нельзя использовать /dev/stderr в качестве конвейера

Проблема не в pasteи не в /dev/stdin. Это с /dev/stderr.

Все команды создаются с одним открытым входным дескриптором (0 :стандартный ввод )и двумя выходами (1 :стандартный вывод и 2 :стандартная ошибка ). Обычно к ним можно получить доступ с именами /dev/stdin, /dev/stdoutи /dev/stderrсоответственно, но см. Насколько переносимы /dev/stdin, /dev/stdout и /dev/stderr? . Многие команды, в том числе paste, также интерпретируют имя файла -как значение STDIN.

Когда вы запускаете bbотдельно, и STDOUT, и STDERR являются консолью, где обычно появляется вывод команды. Строки проходят через разные дескрипторы (, как показано вашим annotate-output), но в конечном итоге оказываются в одном и том же месте.

Когда вы добавляете |и вторую команду, создавая конвейер...

bb | paste /dev/stdin /dev/stderr

|указывает оболочке соединить вывод bbсо входом paste. pasteсначала пытается прочитать из /dev/stdin, который (через некоторые символические ссылки )разрешается в свой собственный стандартный входной дескриптор (, который оболочка только что подключила ), поэтому строка 1проходит.

Но оболочка/конвейер ничего не делает для STDERR. bbпо-прежнему отправляет(e1e2и т. д. )на консоль. Тем временем pasteпытается читать с той же консоли, которая зависает (до тех пор, пока вы что-нибудь не наберете ).

Ваша ссылка Почему я не могу прочитать /dev/stdout в текстовом редакторе? по-прежнему актуален здесь, потому что те же самые ограничения применяются к /dev/stderr.

Как сделать второй конвейер

У вас есть команда, которая выдает как стандартный вывод, так и стандартную ошибку, и вы хотите pasteвывести эти две строки рядом друг с другом. Это означает два параллельных канала, по одному на каждую колонку. Конвейер оболочки ... |...предоставляет один из них, а второй вам нужно будет создать самостоятельно,и перенаправить STDERR на это с помощью 2>filename.

mkfifo RHS
bb 2>RHS | paste /dev/stdin RHS

Если это предназначено для использования в сценарии, вы можете предпочесть создать этот FIFO во временном каталоге и удалить его после использования.

4
27.01.2020, 21:11

Есть пара проблем со всей строкой, которую нам нужно проанализировать, а именно:

seq 2 | tee >(sed 's/^/e/' > /dev/stderr) | paste /dev/stdin /dev/stderr

стдерр

Во-первых, последняя команда. Только stdout может проходить через канал:

$ seq2 | paste -
1
2

$ seq2 | paste - -
1 2

Читать нечегоstderr:

$ seq 2 | paste - /dev/stderr 
1   ^C

Вам нужно ^Cего, потому что он блокирует, из stderrнечего читать.
Даже если вы создаете некоторый вывод для stderr, он не проходит через канал :

.
$ { seq 2; seq 3 4 >/dev/stderr; } | paste - /dev/stderr
1   3
4

Точно так же, как и раньше, 1печатается, а pasteблокируется, ожидая stderr.
Остальные 2 числа отправлялись прямо в консоль и печатались (независимо ).

Вы можете ввести некоторые данные в stderrв последней команде конвейера:

$ { seq 2; seq 3 4 >/dev/stderr; } | paste - /dev/stderr 2</dev/null
1
2
3
4

Кстати, это то же самое, что и 2>/dev/null, чтобы избежать блокировки второго файлового дескриптора, используемого в команде paste. Но напечатанные значения поступают непосредственно из seq 3 4, перенаправленных на консоль, а не из paste.Это делает то же самое:

$ { seq 2; seq 3 4 >/dev/tty; } | paste - /dev/stderr 2</dev/null
1   
2   
3
4

И это не блокирует:

$ seq 2 | tee >(sed 's/^/e/' > /dev/stderr) | 
  paste /dev/stdin /dev/stderr 2</dev/null
1   
2   
e1
e2

заказ

Во-вторых, вывод teeне обязательно должен быть «в порядке».`tee `и `bash `порядок замены процессов

И на самом деле :Результат подстановки процесса не обязательно должен быть «в порядке» :Вывод замены процесса не соответствует порядку

$ echo one; echo two > >(cat); echo three;
one
three
two

Фактически, в некоторых примерах, если вы попробуете несколько раз, вы можете получить разные порядки. недетерминированный вывод -независимых процессов, запущенных одновременно, путем подстановки процессов

$ printf '%s\n' {0..1000} | tee >(head -n2) >(sort -grk1,1 | head -n3) >/dev/null
1000
999
998
0
1

Таким образом, нет, это невозможно сделать с заменой процессов и вставкой.
Вам нужно отдать приказ о казни:

$ seq 2 | { while read a; do printf "%s %s\n" "$a" "e$a" ; done; }
1 e1
2 e2

бб

Итак, ваша функция bb, которая (в основном )содержит:

| tee >(sed 's/^/e/')

Что можно проверить с помощью:

$ printf '%s\n' {0..1000} | tee >(sort -grk1,1 | head -n3 >&2) | head -n 2
0
1
291
290
289

Должен выводить 0, 1, 1000, 999, 998 в таком порядке, но часто это не так.
То есть :Это внутренне в -стабильном.

Устойчивое вещественное решение.

Единственным безопасным решением для bb является избежание любой замены процесса.
И, воспользовавшись тем, что {…}захватывает как stdout, так и stderr, пример:

$ bash -c '{ echo test-str >/dev/stderr; }' 2>/dev/null

Нет вывода, удалите 2 для подтверждения.

Это будет работать для bb:

$ bb() { seq 5 | tee /dev/stderr | sed 's/^/e/'; }

И используйте FIFO для вставки:

$ mkfifo out2
$ bb 2>out2  | paste out2 -
1   e1
2   e2
3   e3
4   e4
5   e5

Вам потребуется установить ловушку для удаления файла fifo и проверить, существует ли файл fifo перед его созданием.

Похоже, работает переносимо во всех оболочках (, совместимых с синтаксисом Almquist ), который я тестировал. Не полностью проверено, запросите подтверждение у других пользователей, могут быть какие-то еще неизвестные сюрпризы.

1
27.01.2020, 21:11

Теги

Похожие вопросы