Рассмотрим следующую команду: bash -c "echo x; cat 1" | tee 1.

Насколько я понимаю, он будет разветвляться на новую оболочку, записывать x в стандартный вывод, записывать file 1 not found в стандартный вывод, выходить и возвращать управление родительскому процессу и записывать x в стандартный вывод и в 1. Следовательно, я ожидаю, что окончательный вывод будет x, а файл 1 содержит именно строку x.

Тем не менее, это не так. На самом деле файл 1 обычно содержит как минимум два экземпляра x, а иногда и тысячи строк x. В пакетном тесте с запуском команды десять тысяч раз среднее число x, записанных в файл, составило 52,3, а медиана — 1. Какая механика вызывает это? Какое распределение вероятностей моделирует такое поведение? Подозреваю, что оно условно геометрическое и в остальном однородное.

10
Will Sherwood 10 Авг 2018 в 21:39
3
Это связано со временем выполнения левой и правой частей конвейера. Оба запускаются одновременно или близко к этому. Если tee открыл файл для записи до того, как cat открыл его для чтения, вы можете получить много x-ов в файле. В этом случае «цикл» будет заканчиваться всякий раз, когда cat читает быстрее, чем tee записывает, достигая конца файла.
 – 
Kusalananda
10 Авг 2018 в 21:42
Из ограниченного тестирования здесь среднее количество x, записанных в файл, составило 4,35. Я думаю, это будет сильно зависеть от загрузки машины.
 – 
Renan
11 Авг 2018 в 03:18

1 ответ

Это очень любопытно, поэтому я попытался исследовать его с помощью strace. Запустите вашу команду в цикле 1000 раз:

mkdir {000..999}
for i in {000..999}; do
echo $i
(cd $i; strace -f -o trace.log bash -c 'bash -c "echo x; cat 1" | tee 1 >/dev/null'; )
done

Нашел файл с наибольшим количеством строк (wc -l */1 | sort -nr | head -n2) и проверил соответствующий trace.log. Я, конечно, вижу много:

7567  <... read resumed> "x\n", 8192)   = 2
7567  write(1, "x\n", 2)                = 2
7567  write(3, "x\n", 2)                = 2
7567  read(0,  <unfinished ...>
7568  read(3, "x\n", 131072)            = 2
7568  write(1, "x\n", 2)                = 2
7567  <... read resumed> "x\n", 8192)   = 2
7567  write(1, "x\n", 2)                = 2
7567  write(3, "x\n", 2)                = 2
7567  read(0,  <unfinished ...>
7568  read(3, "x\n", 131072)            = 2
7568  write(1, "x\n", 2)                = 2
7567  <... read resumed> "x\n", 8192)   = 2
7567  write(1, "x\n", 2)                = 2
7567  write(3, "x\n", 2)                = 2
7567  read(0,  <unfinished ...>

Где 7567 — tee 1, а 7568 — cat 1. Они определенно чередуются, так что да, как и предполагалось, все дело во времени выполнения (и я представляю переключение контекста) двух команд.

1
chutz 25 Сен 2018 в 19:57