Невозможно перенаправить выходной поток

Когда я пытаюсь перенаправить вывод cut он всегда кажется пустым. Если вы не перенаправляете его, вывод отображается в терминале, как ожидалось. Это справедливо для OS X 10.10 и Linux 4.1.6.

Это работает:

 root@karla:~# nc 10.0.2.56 30003 [...] lots of lines [...] 

Это работает:

 root@karla:~# nc 10.0.2.56 30003 | cat [...] lots of lines [...] 

Это работает:

 root@karla:~# nc 10.0.2.56 30003 | cut -d, -f 15,16 [...] lots of lines [...] 

Это не

 root@karla:~# nc 10.0.2.56 30003 | cut -d, -f 15,16 | cat [nothing] 

Это снова

 root@karla:~# cat messung1 | cut -d, -f15,16 | cat [...] lots of lines [...] 

Это не ограничивается cat после cut . grep , tee и стандартное перенаправление с помощью > тоже не работают.

Что там не так?

Дело не в том, что нет выхода, поскольку он идет в кусках.

Как и во многих программах, когда его выход больше не является терминалом, cut буферов выводит его. То есть, он только записывает данные, когда он накапливает заполненный им буфер. Как правило, что-то вроде 4 или 8 kiB, хотя YMMV.

Вы можете легко проверить это, сравнив:

 (echo foo; sleep 1; echo bar) | cut -c2- 

С:

 (echo foo; sleep 1; echo bar) | cut -c2- | cat 

В первом случае cut ouputs oo\n а затем ar\n через секунду, а во втором случае вырезаем выходы oo\nar\n через 1 секунду, то есть когда он видит конец своего ввода и сбрасывает свой вывод при выходе.

В вашем случае, поскольку stdin – nc , он будет видеть только конец своего ввода, когда соединение будет закрыто, поэтому он только начнет выводить что-либо после того, как он накопил данные на 4kiB для записи.

Чтобы обойти это, возможно несколько подходов.

  • В системах GNU или FreeBSD вы можете использовать stdbuf программу stdbuf которая может настраивать поведение буферизации некоторых команд (она не работает для всех, так как использует LD_PRELOAD для предварительной настройки поведения буферизации stdio).

     ... | stdbuf -oL cut -d, -f15,16 | cat 

    сказал бы cut чтобы сделать буферизацию на основе строки на ее stdout.

  • некоторые команды, такие как GNU grep имеют опции, влияющие на их буферизацию. ( --line-buffered в случае GNU grep ).

  • вы можете использовать pseudo-tty wrapper, чтобы заставить stdout команды быть терминалом. Однако большинство этих решений имеют некоторые недостатки и ограничения. Например, сценарий expect unbuffer, часто упоминаемый для решения этой проблемы, имеет несколько ошибок, например.

    Тот, который не работает слишком плохо, – это использование socat as:

     ... | socat -u 'exec:"cut -d, -f15,16",pty,raw' - 
  • Вы можете заменить текстовую утилиту инструментом обработки текста более высокого уровня, который поддерживает небуферизованный вывод.

    • Например, GNU awk имеет функцию fflush() чтобы очистить свой вывод. Итак, ваш cut -d, -f15,16 можно написать:

       awk -F, -vOFS=, '{print $15,$16;fflush()}' 
    • если ваш awk не имеет функции fflush() , вы можете использовать system("") . Обычно это команда для выполнения команды. Здесь мы выполняем пустую команду, но фактически используем ее для того, чтобы awk удалял его stdout перед запуском команды.

    • или вы можете использовать perl :

       perl -F, -lane 'BEGIN{$,=",";$|=1} print @F[14..15]'