1

У меня есть три примера перенаправления stdin/stdout, только один из них работает так, как он предназначен. Я был бы рад, если бы кто-то смог мне это объяснить.

Цель состоит в том, чтобы отсортировать содержимое в file1 и сохранить изменения в том же файле.

  1. сортировать файл1 | tee file1> /dev /null --------> Это работает

  2. сортировать файл1 | тройник file1 --------> Содержимое file1 будет удалено

  3. сортировать файл1 | тройник file1> file2 --------> Содержимое file1 будет удалено

PS. ти копирует стандартный ввод в каждый ФАЙЛ, а также в стандартный вывод.

Что заставляет работать первый пример?

4 ответа4

2

Я сомневаюсь, что это поведение предсказуемо (и, конечно, не будет зависеть от этого). Команда tee вероятно, запускает новый процесс для отправки своего ввода в «другое» место назначения. Операционная система будет «буферизовать» выходные данные до тех пор, пока не достигнет точки, в которой будет создан целевой файл и записан в него временный буфер. Точный момент, когда это происходит (и перезаписывает источник), вероятно, зависит от:

  • Размер файла и доступная память для буфера
  • Прошедшее время
  • Если вход от трубы до tee заканчивается

Это идет глубже, чем bash: так работает программа, которая запускает bash . Оболочка просто интерпретирует введенные вами команды и запускает программы, необходимые для выполнения команд. Оболочка не имеет никакого контроля над тем, как работает каждая программа, и еще меньше - как эти программы взаимодействуют. Пользователь просит программу (или набор программ) взять данные из входного файла и записать результат в один и тот же входной файл в одном предложении.

Не забывайте, что bash - это просто интерпретатор пользовательских команд: это всего лишь shell операционной системы для преобразования намерений пользователя в системные вызовы.

И это тоже задокументировано ! Или это письмо, которое решает подобные проблемы. Или этот поток StackOverflow. Или этот поток Serverfault.

Обратите внимание, что это также может произойти с перенаправлением stdin: если вы берете команды ввода из файла: $ myprog < commandfile . Если myprog пишет в командный файл, нет гарантии, что все команды 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 '^[^.][^.]*\.[^.][^.]*\.[^.][^.]*\.[^.][^.]*$'

Я полагаю, вы сделали бы копию в первую очередь? (команда взята из Advanced Bash-Scripting Guide)

0

В соответствии с моими тестами на Debian Wheezy, все 3 сценария могут привести к обоим результатам (file1 сортируется и записывается обратно в себя ИЛИ ничего не сортируется и ничего не записывается в file1.

Я считаю, что это нормальное поведение и происходит от того, как Linux работает с файлами. Подумайте о команде - команда сортировки начинает читать file1 и немедленно отправляет свой вывод tee. Ти считывает вывод, записывает его обратно в файл1 и печатает его в /dev /null. В случае, если сортировка выполняется достаточно быстро, чтобы прочитать весь файл1, tee получает отсортированный вывод. Но в случае, если он получает блокировку файла, он стирает его (он всегда стирает выходной файл, кроме случаев, когда используется опция добавления), и это в значительной степени происходит во всех трех ваших сценариях.

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

Я бы порекомендовал следующую процедуру:

cat file1 | sort > /tmp/sorting.tmp; mv /tmp/sorting.tmp file1

Если вы хотите увидеть отсортированный вывод на stdout, сделайте это так:

cat file1 | sort | tee /tmp/sorting.tmp; mv /tmp/sorting.tmp file1

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

0

То есть вы хотите, чтобы исходное содержимое файла оставалось при добавлении файла с изменениями?

по умолчанию, попробуйте использовать флаг -a, чтобы добавить файл с изменениями.

0
sort file1 | tee file1 > tmp && mv file1 original && mv tmp file1

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .