Скрыть ошибки кота

Я хотел бы установить скрипт для непрерывного анализа конкретного маркера в XML-файле.

Скрипт содержит следующее while цикл:

function scan_t()
{
INPUT_FILE=${1}
while : ; do
   if [[ -f "$INPUT_FILE" ]]
   then
      ret=`cat ${INPUT_FILE} | grep "<data>" | awk -F"=|>" '{print $2}' | awk -F"=|<" '{print $1}'`
      if [[ "$ret" -ne 0 ]] && [[ -n "$ret" ]]
      then
         ...
      fi
   fi
done
} 
scant_t "/tmp/test.xml"

Формат строки:

<data>0</data> or <data>1</data> <data>2</data> ..

Даже если условие if [[ -f "$INPUT_FILE" ]] был добавлен в скрипт, иногда я получаю:

cat: /tmp/test.xml: такого файла или каталога нет.

Действительно, $INPUT_FILE обычно используется другим процессом, который заряжается для подавления файла после чтения.

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

1 ответ

Если какой-то другой процесс также может прочитать и удалить файл до того, как этот сценарий его увидит, вы разработали свою систему с условием гонки. (Я предполагаю, что "заряженный для подавления" означает "предназначенный для разрыва связи"...)

Если для этого скрипта необязательно видеть каждый входной файл, просто перенаправьте stderr на /dev/null (т. е. игнорировать ошибки, когда укус расы). Если это не является обязательным, попросите этот сценарий переименовать входной файл во что-то другое, а другой процесс следит за этим. Перед тем как переименовать, проверьте, существует ли этот файл, чтобы не перезаписывать файл, который другой процесс еще не прочитал.


У вашей петли ужасный дизайн. Во-первых, вы заняты ожидания (без sleep на всех) на файл вступает в существование. Во-вторых, когда есть вход, вы запускаете 4 программы вместо 1.

Ожидание занятости можно избежать с помощью inotifywait смотреть каталог на предмет изменений. Итак if [[ -f $INPUT_FILE ]] тело цикла запускается только после изменения каталога, а не так быстро, как ядро ​​процессора может его запустить.

Второй проще решить: никогда cat file | something, Или something file, или же something < file если something не принимает имена файлов в командной строке или ведет себя по-другому. cat полезно только если у вас есть несколько файлов для объединения. Для чтения файла в переменную оболочки используйте foo=$(<file),

Я вижу по комментариям, что вам уже удалось превратить весь ваш конвейер в одну команду. Так пиши

INPUT_FILE=foo;
inotifywait -m -e close_write -e moved_to --format %f . |
while IFS= read -r event_file;do
    [[ $event_file == $INPUT_FILE ]] &&
       awk -F '[<,>]' '/data/ {printf "%s ",$3} END {print ""}' "$INPUT_FILE" 2>/dev/null

     #  echo "$event_file" &&
     #  date;
done
# tested and working with the commented-out echo/date commands

Обратите внимание, что я жду close_write и move_to, а не других событий, чтобы избежать прыжка с пистолета и чтения файла, который еще не закончен во время записи. Положил $INPUT_FILE в своем собственном каталоге, так что вы не получите ложноположительных событий, пробуждающих ваш цикл для других имен файлов.

Чтобы также реализовать предложение переименовать для ввода для следующего этапа, вы должны поместить while [[ -e $INPUT2 ]]; do sleep 0.2; done; mv -n "$INPUT_FILE" "$INPUT2" цикл ожидания ожидания после awk.


Альтернативой было бы запустить inotifywait один раз за итерацию цикла, но у вас есть шанс застрять $INPUT_FILE создан раньше inotifywait начал смотреть. Таким образом, производитель будет ждать, пока потребитель потребит, а потребитель не увидит событие.

# Race condition with an asynchronous producer, DON'T USE
while inotifywait -qq -e close_write -e moved_to; do
    [[ $event_file == $INPUT_FILE ]] &&
       awk -F '[<,>]' '/data/ {printf "%s ",$3} END {print ""}' "$INPUT_FILE" 2>/dev/null
done

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


Если у вас нет inotifywait, вы можете просто поставить sleep в петлю. GNU sleep поддерживает доли секунды, например sleep 0.5, Busybox, вероятно, нет. Вы, возможно, захотите написать крошечную тривиальную программу на C, которая постоянно пытается open(2) файл в цикле, который включает в себя usleep или же nanosleep, когда open успешно, перенаправить стандартный ввод из этого, и exec ваш awk программа. Таким образом, между stat и open,

#include <unistd.h>    // for usleep/dup2

#include <sys/types.h>  // for open
#include <sys/stat.h>
#include <fcntl.h>

#include <errno.h>
#include <stdio.h>  // for perror

void waitloop(const char *path)
{
    const char *const awk_args[] = { "-F", "[<,>]",
         "/data/ {printf \"%s \",$3} END {print \"\"}",
         path
    };
    while(42) {
        int fd = open(path, O_RDONLY);
        if (-1 != fd) {
            // if you fork() here, you can avoid the shell loop too.

            dup2(fd, 0);  // redirect stdin from fd.  In theory should check for error here, too.
            close(fd);   // and do this in the parent after fork
            execv("/usr/bin/awk", (char * const*)awk_args);  // execv's prototype doesn't prevent it from modifying the strings?
        } else if(errno != ENOENT) {
            perror("opening the file");
        } // else ignore ENOENT
        usleep(10000);  // 10 milliseconds.
    }

}
// optional TODO: error-check *all* the system calls.

Это компилируется, но я не проверял это. Цикл внутри одного процесса делает open / usleep гораздо легче, чем весь процесс, чтобы сделать sleep 0.01 из скорлупы.

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

Другие вопросы по тегам