Разница между cat и '>' для обнуления файла

23

Различаются ли эти две команды в том, как они обнуляют файлы? Является ли последний более короткий способ сделать первый? Что происходит за кулисами?

И то и другое

$ cat /dev/null > file.txt

$ > file.txt 

Уступать

-rw-r--r--  1 user  wheel  0 May 18 10:33 file.txt
KM.
источник

Ответы:

28

cat /dev/null > file.txtэто бесполезно использование кошки .

В основном cat /dev/nullпросто catничего не выводится. Да, это работает, но многие его осуждают, потому что это приводит к вызову внешнего процесса, в котором нет необходимости.
Это одна из тех вещей, которая распространена просто потому, что она распространена.

Использование just > file.txtбудет работать на большинстве оболочек, но оно не полностью переносимо. Если вы хотите полностью портативный, следующие варианты являются хорошими:

true > file.txt
: > file.txt

Оба :и не trueвыводят данных, и являются встроенными оболочками (тогда catкак это внешняя утилита), таким образом, они легче и более «правильны».

 

Обновить:

Как упомянул Тайлер в своем комментарии, есть также >| file.txtсинтаксис.

У большинства оболочек есть настройка, которая не позволяет им обрезать существующий файл с помощью >. Вы должны использовать >|вместо этого. Это сделано для предотвращения человеческих ошибок, когда вы действительно хотели добавить >>. Вы можете включить поведение с помощью set -C.

Итак, с этим, я думаю, самый простой, самый правильный и переносимый метод усечения файла будет:

:>| file.txt
Патрик
источник
2
Команда двоеточия определена в POSIX . Это нулевая операция, которая существует для расширения аргументов командной строки.
Кодзиро
3
Лол, «оскорбление кота»
км.
2
POSIX :также обязывает @kojiro быть встроенным, и фактически отличается от trueтого, что считается «специальным» встроенным .
jw013
2
не забывай о ноклобере . >| fileявляется более явным усечением.
Тайлер
1
Нет trueне обязательно быть встроенным, и это традиционно не было. :построен во всех снарядах семьи Борн. :это специальная встроенная функция для POSIX (поэтому : > fileона выйдет из оболочки, например, если fileне может быть открыта для записи в оболочках POSIX) и trueне является. POSIX даже упоминает, что это :может быть более эффективным, чем trueв некоторых системах.
Стефан Шазелас
23

С точки зрения портативности:

                      Bourne POSIX  zsh    csh/tcsh  rc/es  fish
> file                Y      Y      N(1)   N(1)      N      N
: > file              N/Y(2) Y(3)   Y      Y(4)      N(5)   N(5)
true > file           Y(5)   Y      Y      Y(5)      Y(5)   Y(5)
cat /dev/null > file  Y(5)   Y      Y(5)   Y(5)      Y(5)   Y(5)
eval > file           Y(3,8) Y(3)   Y      Y(6)      Y      Y
cp /dev/null file (7) Y(5)   Y      Y(5)   Y(5)      Y(5)   Y(5)
printf '' > file      Y(5)   Y      Y      Y(5)      Y(5)   Y

Заметки:

  1. за исключением случаев эмуляции shили kshэмуляции, для перенаправлений без команды в zsh предполагается команда по умолчанию (в catпротивном случае это пейджер только для перенаправления stdin ), которая может быть настроена с помощью переменных NULLCMD и READNULLCMD. Это вдохновлено от аналогичной функции в(t)csh
  2. Перенаправления изначально не выполнялись :в UnixV7, поскольку :интерпретировались на полпути между лидером комментария и пустой командой. Позже они были и, как и для всех встроенных, если перенаправление не удается, что выходит из оболочки.
  3. :и, evalбудучи специальными встроенными модулями, в случае сбоя перенаправления, он выходит из оболочки ( bashтолько в режиме POSIX).
  4. Интересно, (t)cshчто это определяет нулевую метку (для goto), так что goto ''там будет ответвление. Если перенаправление не удается, это выходит из оболочки.
  5. Если / , если соответствующая команда доступна в $PATH( как :правило , не является, true, cat, cpи как printfправило , не являются (POSIX требует от них)).
  6. Если перенаправление не удается, это выходит из оболочки.
  7. Однако, если fileэто символическая ссылка на несуществующий файл, некоторые cpреализации, такие как GNU, откажутся создавать его.
  8. Первые версии оболочки Bourne не поддерживали перенаправление встроенных функций

С точки зрения разборчивости:

(этот раздел очень субъективен)

  • > file, Это >слишком похоже на подсказку или комментарий. Кроме того, вопрос, который я задам, читая этот вопрос (и большинство оболочек будут жаловаться на то же самое), заключается в том, какой именно вывод вы перенаправляете? ,
  • : > file, :известен как команда no-op. Так что сразу читается как генерация пустого файла. Однако и здесь это :легко можно пропустить и / или рассматривать как подсказку.
  • true > file: что имеет логическое отношение к перенаправлению или содержимому файла? Что здесь подразумевается? это первое, что приходит мне в голову, когда я читаю это.
  • cat /dev/null > file, Объединить /dev/nullв file? catкоторые часто видели , как команда , чтобы сбросить содержимое файла, который все еще может иметь смысл: сбросить содержимое в пустой файл вfile , немного как свернутой способ сказать , cp /dev/null fileно все - таки понятно.
  • cp /dev/null file, Копирует содержимое пустого файла в file. Имеет смысл, хотя кто - то , не зная , как cpэто означало , чтобы сделать по умолчанию может подумать , что вы пытаетесь сделать fileна nullустройство , а также.
  • eval > fileили eval '' > file. Ничего не запускается и перенаправляет вывод в file. Имеет смысл для меня. Странно, что это не распространенная идиома.
  • printf '' > file: явно ничего не печатает в файл. Тот, который имеет больше всего смысла для меня.

С точки зрения производительности

Разница будет в том, будем ли мы использовать встроенную оболочку или нет. Если нет, процесс должен быть разветвлен, команда загружена и выполнена.

evalгарантированно будет построен во всех снарядах. :встроен везде, где он доступен (любит Bourne / csh). trueвстроен только в Bourne-подобные оболочки.

printfвстроен в большинство современных борноподобных оболочек и fish.

cpи catвообще не являются встроенными.

Теперь cp /dev/null fileне вызывает перенаправления оболочки, поэтому такие вещи, как:

find . -exec cp /dev/null {} \;

будут более эффективными, чем:

find . -exec sh -c '> "$1"' sh {} \;

(хотя не обязательно чем:

find . -exec sh -c 'for f do : > "$f"; done' sh {} +

).

Лично

Лично я использую : > fileв оболочках типа Борна, и в эти дни я не использую ничего, кроме оболочек типа Борна.

Стефан Шазелас
источник
Как насчет dd of=file count=0?
Кодзиро
2
@kojiro, в некоторых реализациях dd(как минимум в Solaris 10) count=0игнорируется. dd if=/dev/null of=fileбудет более портативным. В любом случае, это не зависит от оболочки.
Стефан Шазелас
Хорошо, но это не менее заслуживает включения, чем cp /dev/null file, верно?
Кодзиро
2
@kojiro, cp /dev/null fileэто распространенная идиома. Я ограничиваюсь тем, что дело не в перечислении всех возможных путей.
Стефан Шазелас
5

Возможно, вы захотите посмотреть truncate, что делает именно это: урезать файл.

Например:

truncate --size 0 file.txt

Это, вероятно, медленнее, чем при использовании true > file.txt.

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

Fabian
источник
2
Truncate - это хорошо, когда вы хотите обрезать файл до значения, отличного от 0. Тем не менее, даже без оболочки это странное утверждение: можете ли вы описать контекст, в котором truncateбудут доступны, но ни библиотеки C, >ни unistdбиблиотеки не будут доступны?
Кодзиро
На самом деле, нет. Вероятно, есть более элегантное решение для каждого доступного скрипта или языка программирования.
Фабиан
3
truncateявляется утилитой FreeBSD, сравнительно недавно (2008 г.) добавленной в GNU coreutils (хотя --sizeстиль длинных опций GNU специфичен для GNU), поэтому она недоступна в системах, отличных от GNU или FreeBSD, и недоступна в более старых системах GNU, Я бы не сказал, что он портативный. cp /dev/null fileбудет работать без перенаправления оболочки и будет более переносимым.
Стефан Шазелас
Хорошо, я удалю этот комментарий о переносимости. Хотя ваше определение недавнего кажется другим.
Фабиан
2

Ответ немного зависит от того file.txt, что есть, и как процесс записи в него!

Я приведу пример общего использования: у вас есть растущий файл журнала file.txt, и вы хотите повернуть его.

Поэтому вы копируете, например, file.txtв file.txt.save, а затем усекаете file.txt.

В этом случае, если файл не открывается another_process(например: это another_processможет быть программа, выводящая в этот файл, например, программа, регистрирующая что-то), тогда ваши 2 предложения эквивалентны, и оба работают хорошо (но второе предпочтительнее, чем first "cat / dev / null> file.txt" - это бесполезное использование Cat, а также открывает и читает / dev / null).

Но настоящая проблема была бы, если other_processон все еще активен и все еще имеет открытый дескриптор, идущий к файлу file.txt.

Затем возникают 2 основных случая, в зависимости от того, как был other processоткрыт файл:

  • Если other_processоткрыть его обычным способом, дескриптор все равно будет указывать на прежнее место в файле, например, со смещением 1200 байтов. Поэтому следующая запись начнется со смещения 1200, и, таким образом, у вас снова будет файл размером 1200 байт (+ что бы ни писал другой_процесс) с 1200 ведущими нулевыми символами! Не то, что вы хотите , я полагаю.

  • Если other_processоткрыть file.txtв «режиме добавления», то каждый раз, когда он пишет, указатель будет активно искать в конце файла. Поэтому, когда вы усекаете его, он будет «искать» до байта 0, и у вас не будет плохого побочного эффекта! Это то, что вы хотите (... обычно!)

Обратите внимание, что это означает, что вам необходимо при усечении файла убедиться, что все, кто все other_processеще пишет в это место, открыли его в режиме «добавления». В противном случае вам нужно будет остановить их other_processи запустить снова, чтобы они указывали на начало файла, а не на прежнее место.

Ссылки: /programming//a/16720582/1841533 для более ясного объяснения и хорошего короткого примера различия между ведением журнала в обычном режиме и в режиме добавления на /programming//a/984761/1841533

Оливье Дюлак
источник
2
Очень мало этого ответа на самом деле относится к вопросу или отвечает на него. Разница между a cat /dev/null > fileи a > fileесть a, cat /dev/nullи это не имеет значения для файла.
jw013
@ jw013: правда! Но я просто хотел воспользоваться возможностью вопроса, чтобы переформулировать информацию «что вы хотите / не то, что вы хотите», так как она не очень известна и может сильно ударить по кому-то, пытающемуся вращать журналы (частый случай, когда вы хотите обрезать файл).
Оливье Дюлак
1
Есть время и место для всего. Ваша информация может быть полезна в каком-то другом контексте, но она не относится к этому - вы должны найти более подходящее место для нее, потому что никто, пытающийся вращать журналы, не будет искать этот совершенно не связанный вопрос перенаправления. Здесь ваш ответ эквивалентен цифровому сорняку, так же как иное полезное тыквенное растение посреди кукурузного поля будет считаться сорняком.
jw013
1

Мне это нравится, и я часто его использую, потому что он выглядит чище, а не потому, что кто-то случайно нажал клавишу возврата:

echo -n "" > file.txt

Должен быть встроенным тоже?

awsm
источник
3
Есть много способов обнуления файла. Я думаю, что КМ. был заинтересован только в понимании разницы между двумя методами, показанными в вопросе.
DRS
6
Многие echoреализации не поддерживают -n(и выводили бы -n<SPC><NL>здесь. printf '' > file.txtБыли бы более переносимыми (по крайней мере, в современных системах / POSIX).
Стефан Шазелас