У меня есть три примера перенаправления stdin/stdout, только один из них работает так, как задумано. Я был бы рад, если бы кто-нибудь мог мне это объяснить.
Цель — отсортировать содержимое в файле file1 и сохранить изменения в том же файле.
sort file1 | tee file1 > /dev/null --------> Это работает
sort file1 | tee file1 --------> Содержимое файла file1 будет удалено
сортировать файл1 | tee файл1 > файл2 --------> Содержимое файла1 будет удалено
PS. tee копирует стандартный ввод в каждый ФАЙЛ, а также в стандартный вывод.
Что делает первый пример работающим?
решение1
Согласно моим тестам на Debian Wheezy, все 3 сценария могут привести к обоим результатам (файл file1 сортируется и записывается обратно в себя же, ИЛИ ничего не сортируется и ничего не записывается в file1).
Я считаю, что это нормальное поведение, и оно исходит из того, как Linux работает с файлами. Подумайте о команде — команда sort начинает читать file1 и немедленно отправляет свой вывод в tee. Tee считывает вывод, записывает его обратно в file1 и печатает в /dev/null. В случае, если sort достаточно быстр, чтобы прочитать весь file1, tee получает отсортированный вывод. Но в случае, если tee получает свою блокировку на файле, он стирает его (tee всегда стирает выходной файл, за исключением случаев, когда используется опция append), и это примерно то, что происходит во всех ваших 3 сценариях.
Чтобы сделать это короче, скажем, что иногда sort недостаточно быстр, чтобы прочитать file1. В таком случае tee стирает файл ДО того, как sort сможет его прочитать.
Я бы рекомендовал следующую процедуру:
cat file1 | sort > /tmp/sorting.tmp; mv /tmp/sorting.tmp file1
Если вы хотите увидеть отсортированный вывод на stdout, сделайте это следующим образом:
cat file1 | sort | tee /tmp/sorting.tmp; mv /tmp/sorting.tmp file1
Не очень хорошая идея позволять двум разным командам работать с одним файлом в многопроцессорных системах — вы никогда не можете быть уверены, какая из них будет выполнена первой. В однопоточной системе поведение будет иным — последовательным.
решение2
Я сомневаюсь, что такое поведение предсказуемо (и, конечно, не будет зависеть от него). Команда, tee
вероятно, запускает новый процесс для отправки своего ввода в «другое» место назначения. Операционная система будет «буферизировать» вывод, пока не достигнет точки, где она создаст файл назначения и запишет свой временный буфер в файл. Точный момент, когда это произойдет (и перезапишет источник), вероятно, зависит от:
- Размер файла и доступная память для буфера
- Прошедшее время
- Если вход из трубы
tee
заканчивается
Это глубже, чем bash
: Это то, как работают программы, которые bash
начинаются. Оболочка просто интерпретирует команды, которые вы вводите, и запускает программы, необходимые для выполнения команд. Оболочка не контролирует, как работает каждая программа, и еще меньше — как эти программы взаимодействуют. Пользователь несет ответственность за то, чтобы попросить программу (или набор программ) взять данные из входного файла и записать результат в тот же входной файл в том же предложении.
Не забывайте, что bash — это всего лишь интерпретатор пользовательских команд: это всего лишь shell
инструмент операционной системы для преобразования намерений пользователя в системные вызовы.
И егозадокументировано, тоже! Илиэта почта, который решает похожие проблемы. Или этоТема StackOverflow. Илиэта тема Serverfault.
Обратите внимание, что это также может произойти с перенаправлением stdin
: если вы берете входные команды из файла: $ myprog < commandfile
. Если myprog
записывает в commandfile, нет гарантии, что все commandfile
команды будут выполнены.
По-настоящему простой аналогией будет что-то вроде этого списка инструкций:
- Execute the instructions step by step
- Dip this instruction list in a bucket of black paint
- Type in the following commands:
find /etc -type f -exec cat '{}' \; | tr -c '.[:digit:]' '\n' \
| grep '^[^.][^.]*\.[^.][^.]*\.[^.][^.]*\.[^.][^.]*$'
Я полагаю, вы сначала сделаете копию? (команда взята изРасширенное руководство по написанию сценариев Bash)
решение3
То есть вы хотите сохранить исходное содержимое файла, добавив в него изменения?
tee по умолчанию записывает, попробуйте использовать флаг -a для добавления файла с изменениями.
решение4
sort file1 | tee file1 > tmp && mv file1 original && mv tmp file1
Вы можете записать файл в заполнитель, переименовать оригинал в резервную копию, а затем переместить заполнитель в оригинал.