Странное поведение перенаправления, позволяющее усечение вывода

#c #file #unix

#c #файл #unix

Вопрос:

У нас есть скрипт сборки, который создает встроенную систему для нескольких типов плат, и при захвате выходных данных одним способом, кажется, все работает нормально. С другой стороны, он частично проходит через вывод, а затем усекает файл перед продолжением.

Как это работает:

 time ( cd ~ ; builder.sh 2>amp;1 | tee ~/builder.out )
  

И способ, которым это, кажется, усекается:

 time ( cd ~ ; builder.sh > ~/builder.out 2>amp;1 )
  

Усечение, похоже, происходит в очень специфической точке, первая строка в файле после усечения всегда DEFAULT_INCDIRS=... from qmake . Тот факт, что это происходит в определенный момент процесса, а не когда файл достигает определенного размера, похоже, указывает на то, что усечение выполняется не какой-то внешней проверкой файлов.

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

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

В обоих приведенных выше случаях скрипт builder фактически не знает о своем выходном файле, он просто записывает как выходные данные, так и сообщения об ошибках в stdout и позволяет перенаправлению оболочки позаботиться об этом.

Итак, мои вопросы таковы: есть ли способ сделать это в модели ввода-вывода файлов UNIX (скажем, из вызовов C file API)? Другими словами, можете ли вы усечь файл, в который вы записываете, когда он был настроен с помощью перенаправления? Почему tee вариант работает? Что мешает его усечению?

Ответ №1:

Итак, да, кто-то явно вызывает lseek() или ftruncate() включен stdout , как вы уже заметили.

Чтобы отследить нарушителя, strace -f безусловно, поможет. При выполнении чего-то столь экзотического, как это, вам может понадобиться сделать strace -f sh -c 'build.sh 2>amp;1 | cat > output' > log 2>amp;1 , потому что в противном случае он с радостью сожрет ваш вывод strace.

Как только у вас будет журнал, найдите вызов lseek(1, , lseek(2, , ftruncate(1, или ftruncate(2, . Оттуда выполните поиск в обратном направлении к предыдущему exec , и вы должны знать.

Одна из программ, которая на законных основаниях выполняет игры с stdout is cdrecord , по крайней мере, некоторые версии которой хотят, чтобы ваш CD-диск записывался на стандартный вывод.

Комментарии:

1. Наш процесс сборки занимает 40 минут на плату, я не уверен, что у нас есть возможность хранить данные на 40 минут, но я посмотрю, что смогу найти 🙂

2. @paxdiablo: Используйте ssh и передавайте его куда-нибудь еще, например, на свою рабочую станцию, если это необходимо.

Ответ №2:

Хорошо, оказывается, что программы могут выполнять поиск по стандартному выводу (хотя я удивляюсь здравомыслию программ, которые это делают).

Следующая программа иллюстрирует это:

 #include <stdio.h>

int main(void){
    for (int i = 3; i > 0; --i) {
        //rewind(stdout);
        printf("Hello, world %d !n", i);
    }
    return 0;
}
  

Запустите это, записывая выходные данные, и в итоге вы получите файл, содержащий:

 Hello, world 3 !
Hello, world 2 !
Hello, world 1 !
  

Однако, если вы раскомментируете rewind строку, в конечном итоге вы получите только последнюю строку в выходном файле.

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

 myprog >outfile 2>amp;1
  

и, myProg сократив файл, я могу вместо этого сделать:

 myprog 2>amp;1 | cat >outfile
  

и конвейер защитит cat выходной файл от усечения.


С точки зрения фактического вопроса, похоже, что qt5base (часть buildroot ) по какой-то причине использует какие-то махинации с дескриптором выходного файла. Мы решили это с помощью cat метода, описанного выше, поскольку у нас нет времени запускать buildroot (или создавать файлы исправлений), чтобы исправить это должным образом.

Комментарии:

1. Вы также можете попробовать myprog >> outfile 2>amp;1 . Большинство оболочек реализуют это, открывая outfile в режиме только добавления, в котором все записи принудительно выполняются в конце файла независимо от того, что сделал lseek.