Странное поведение перенаправления, позволяющее усечение вывода - PullRequest
2 голосов
/ 08 апреля 2019

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

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

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

И путь, который кажется укороченным:

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

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

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

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

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

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

Ответы [ 2 ]

3 голосов
/ 08 апреля 2019

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

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

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

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

1 голос
/ 08 апреля 2019

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

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

#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>&1

и с усечением файла myProg я могу вместо этого сделать:

myprog 2>&1 | cat >outfile

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


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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...