Чтение именованной трубы: tail или cat?

Основываясь на предыдущих ответах, я обновил следующий сценарий как bash, который работает на Debian Jessie

#!/bin/bash
HOSTNAME=dynamichost.domain.com
LOGFILE=$HOME/ufw.log
Current_IP=$(host $HOSTNAME | head -n1 | cut -f4 -d ' ')

if [ ! -f $LOGFILE ]; then
    /usr/sbin/ufw allow from $Current_IP to any port 22 proto tcp
    echo $Current_IP > $LOGFILE
else

    Old_IP=$(cat $LOGFILE)
    if [ "$Current_IP" = "$Old_IP" ] ; then
        echo IP address has not changed
    else
        /usr/sbin/ufw delete allow from $Old_IP to any port 22 proto tcp
        /usr/sbin/ufw allow from $Current_IP to any port 22 proto tcp
        echo $Current_IP > $LOGFILE
        echo iptables have been updated
    fi
fi
7
17.09.2017, 14:00
2 ответа

Когда вы делаете:

cat fifo

Предполагая, что ни один другой процесс еще не открыл fifoдля записи, catзаблокирует системный вызов open(). Когда другой процесс открывает файл для записи, создается экземпляр канала и возвращается open(). catбудет вызывать read()в цикле, а read()будет блокироваться до тех пор, пока какой-либо другой процесс не запишет данные в конвейер.

catувидит конец -файла -(eof ), когда все остальные процессы записи закроют свой файловый дескриптор для fifo. В каких точках catзаканчивается и труба разрушается¹.

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

В:

tail -f file

Как и cat, tailбудет ждать, пока процесс откроет файл для записи. Но здесь, поскольку вы не указали -n +1для копирования с самого начала, tailнужно будет дождаться eof, чтобы узнать, какими были последние 10 строк, поэтому вы ничего не увидите, пока не закончится запись. закрыто.

После этого tailне будет закрывать свой fd для канала, что означает, что экземпляр канала не будет уничтожен, и по-прежнему будет пытаться читать из канала каждую секунду (в Linux, этого опроса можно избежать. с помощью inotifyи некоторые версии GNU tailделают это там ). Это read()сразу вернется с eof (, поэтому вы видите 100% CPU с -s 0(, что с GNU tailозначает не ждать между read()с вместо ожидания одной секунды ))пока какой-либо другой процесс снова не откроет файл для записи.

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

cat 0<> fifo # the 0 is needed for recent versions of ksh93 where the
             # default fd changed from 0 to 1 for the <> operator

catstdin будет открыт как для чтения, так и для записи, что означает, что catникогда не увидит на нем eof (он также сразу создает экземпляр канала, даже если нет другого процесса, открывающего fifoдля записи ).

В системах, где это не работает, вместо этого можно использовать:

cat < fifo 3> fifo

Таким образом, как только какой-либо другой процесс откроет fifoдля записи, будет возвращено первое чтение -только open(), после чего оболочка выполнит запись -только open()до запуск cat, что предотвратит повторное разрушение трубы.

Итак, подводя итог:

  • по сравнению с cat file, она не останавливалась после первого раунда.
  • по сравнению сtail -n +1 -f file:это не будет делать бесполезный read()каждую секунду после первого раунда, никогда не будет eof на одном экземпляре канала, не будет задержки до одной секунды, когда второй процесс открывает канал для записи после того, как первый его закрыл.
  • по сравнению с tail -f file. В дополнение к вышесказанному, ему не нужно было бы ждать окончания первого раунда, прежде чем что-то выводить (только последние 10 строк ).
  • по сравнению с cat fileв цикле будет только один экземпляр канала. Окон гонки, упомянутых в ¹, следует избегать.

¹ в этот момент между последним read(), указывающим eof, и cat, завершающим и закрывающим конец канала для чтения, на самом деле есть небольшие окна, в течение которых процесс может открыть fifoдля записи снова (и не быть заблокированным, так как все еще есть конец чтения ). Затем, если он что-то записывает после завершения catи до того, как другой процесс откроет fifoдля чтения, он будет уничтожен с помощью SIGPIPE.

12
27.01.2020, 20:17

Позвольте мне предложить другое решение. Pipe будет доступен для чтения до тех пор, пока какой-то процесс будет писать на второй конец. Так можно создать какой-нибудь фейк catв фоновом режиме (или в другом терминале ), например:

mkfifo fifo
cat >fifo &
cat fifo

Теперь можно сколько угодно писать в fifo, а когда закончите просто убить текущий catс помощью C -c , а потом fgвывести сначала catиз фон и, наконец, C -d , чтобы остановить его.

2
27.01.2020, 20:17

Теги

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