Почему `cp` предназначен для автоматической перезаписи существующих файлов? [закрыто]

30

Я проверил cpс помощью следующих команд:

$ ls
first.html   second.html  third.html

$ cat first.html
first

$ cat second.html
second

$ cat third.html
third

Затем я копирую first.htmlв second.html:

$ cp first.html second.html

$ cat second.html
first

Файл second.htmlмолча перезаписывается без ошибок. Однако, если я сделаю это в графическом интерфейсе рабочего стола, перетащив файл с тем же именем, он будет добавлен first1.htmlавтоматически. Это позволяет избежать случайной перезаписи существующего файла.

Почему не cpследует этому шаблону вместо того, чтобы перезаписывать файлы без вывода сообщений?

Алгебра
источник
10
Я полагаю, что только дизайнеры coreutils могут действительно ответить на этот вопрос, но сейчас это так. Обычно приложения создаются с учетом того, что пользователь действительно означает, что они делают, и сводят к минимуму дополнительные подсказки. Если вы хотите изменить поведение, используйте псевдоним «cp» для «cp -i» или «cp -n».
kevlinux
8
@kevlinux Разработчики coreutils только внедряют стандарт POSIX.
Кусалананда
17
Потому что, когда она была разработана, люди хотели быть максимально краткими с тем, что они делают (следовательно, не копировать), и знали, что они делали, и когда они делали ошибки, они не пытались обвинять инструменты. Тогда это были совершенно разные люди, которые занимались компьютерами. Это все равно, что спросить, почему скальпель для кардиохирурга тоже может разрезать руки.
PlasmaHH
4
Unix был разработан и для компьютерных экспертов, предполагая, что пользователь знает, что он делает. Операционная система будет делать именно то, что сказал пользователь, если это возможно, - не держа его за руку и не запрашивая бесконечных подтверждений. Если операция что-то перезаписывала, предполагалось, что именно этого хотел пользователь. Также помните, что это было в начале 1970-х - до MS DOS, Windows и домашних компьютеров - руководство и удерживание руки пользователя на каждом шагу не было обычным делом. Кроме того, с обработкой телетайпа в качестве терминалов запрашивать подтверждения всегда было бы слишком громоздким.
Баард Копперуд
10
Не псевдоним cpдля cp -iили подобное , потому что вы будете привыкать к тому , подстраховка, что делает системы , в которых он не доступен (большинство из них) , что гораздо более рискованными. Лучше учить себя регулярно cp -iи т. Д., Если это то, что вы предпочитаете.
Рейд

Ответы:

52

Поведение перезаписи по умолчанию cpуказано в POSIX.

  1. Если исходный_файл имеет тип обычный файл, должны быть предприняты следующие шаги:

    3.a. Поведение не определено, если dest_file существует и был записан на предыдущем шаге. В противном случае, если dest_file существует, должны быть предприняты следующие шаги:

    3.ai Если включена опция -i, утилита cp должна написать приглашение к стандартной ошибке и прочитать строку из стандартного ввода. Если ответ не является положительным, cp больше ничего не делает с source_file и переходит к любым оставшимся файлам.

    3.a.ii. Файловый дескриптор для dest_file должен быть получен путем выполнения действий, эквивалентных функции open (), определенной в томе системных интерфейсов POSIX.1-2017, вызываемому с использованием dest_file в качестве аргумента пути, и побитового ИЛИ для O_WRONLY и O_TRUNC в качестве офлаг аргумент.

    3.a.iii. Если попытка получить файловый дескриптор не удалась и включена опция -f, cp попытается удалить файл, выполнив действия, эквивалентные функции unlink (), определенной в томе System Interfaces POSIX.1-2017, который вызывается с помощью dest_file. в качестве аргумента пути. Если эта попытка завершится успешно, cp продолжит с шага 3b.

Когда была написана спецификация POSIX, уже существовало большое количество сценариев со встроенным допущением для поведения перезаписи по умолчанию. Многие из этих сценариев были разработаны для запуска без непосредственного участия пользователя, например, в качестве заданий cron или других фоновых задач. Изменение поведения сломало бы их. Просмотр и изменение их всех для добавления возможности принудительной перезаписи в любом месте, вероятно, считалось огромной задачей с минимальными преимуществами.

Кроме того, командная строка Unix всегда была разработана для того, чтобы опытный пользователь мог эффективно работать, даже за счет сложной кривой обучения для начинающего. Когда пользователь вводит команду, компьютер должен ожидать, что пользователь действительно это подразумевает, без каких-либо догадок; Пользователь обязан быть осторожным с потенциально разрушительными командами.

Когда была разработана оригинальная Unix, тогда системы имели настолько мало памяти и запоминающего устройства по сравнению с современными компьютерами, что перезаписывали предупреждения и подсказки, вероятно, рассматривались как расточительная и ненужная роскошь.

Когда был написан стандарт POSIX, прецедент был твердо установлен, и разработчики стандарта хорошо знали о достоинствах не нарушая обратную совместимость .

Кроме того, как уже описывали другие, любой пользователь может добавить / включить эти функции для себя, используя псевдонимы оболочки или даже создав cpкоманду замены и изменив их, $PATHчтобы найти замену перед стандартной системной командой, и таким образом получить защитную сеть, если желательно.

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

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

Если вы также навязываете запросы в сценариях, что будет делать команда, когда она запускается в контексте, в котором нет пользователя, например, фоновых процессов или заданий cron? Будет ли скрипт зависать, прерываться или перезаписывать?

Зависание или прерывание означает, что задача, которая должна была быть выполнена автоматически, не будет выполнена. Отсутствие перезаписи может иногда также вызывать проблему само по себе: например, это может привести к тому, что старые данные будут дважды обрабатываться другой системой вместо их замены на обновленные данные.

Большая часть возможностей командной строки заключается в том, что как только вы узнаете, как что-то делать в командной строке, вы также будете неявно знать, как сделать это автоматически с помощью сценариев . Но это верно только в том случае, если команды, которые вы используете в интерактивном режиме, также работают точно так же, когда вызываются в контексте скрипта. Любые существенные различия в поведении между интерактивным использованием и использованием сценариев создадут своего рода когнитивный диссонанс, который раздражает опытного пользователя.

Телком
источник
54
"Почему это работает так?" «Потому что так говорится в стандарте». "Почему стандарт так говорит?" «Потому что это уже сработало, понравилось это».
Батист Канделль
16
Последний абзац является настоящей причиной. Диалоги подтверждения и « Вы действительно хотите это сделать? » - подсказки для слабаков :-)
TripeHound
@BaptisteCandellier - Согласен. Это похоже на конечную причину, но мучительно просто вне досягаемости этого ответа.
TED
2
Этот последний абзац почему rm -rfтак эффективен, даже если вы на самом деле не хотели запускать его в своем домашнем каталоге ...
Макс Вернон,
2
@TED ​​Забавно, что никто никогда не упоминает, как системный вызов unlink (2) также «не может» спросить «Мать, могу ли я?» Для подтверждения всякий раз, когда эти семитерные дискуссии снова поднимают их изящные головы. :)
tchrist
20

cpпроисходит от начала Unix. Это было задолго до того, как был написан стандарт Posix. Действительно: Posix только что формализовал существующее поведение cpв этом отношении.

Мы говорим об Эпохе (1970-01-01), когда мужчины были настоящими мужчинами, женщины были настоящими женщинами и пушистыми маленькими существами ... (Я отвлекся). В те дни добавление дополнительного кода делало программу больше. Тогда это было проблемой, потому что первым компьютером, на котором работал Unix, был PDP-7 (с возможностью обновления до 144 КБ ОЗУ!). Так что все было маленьким, эффективным, без функций безопасности.

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

(Есть замечательный мультфильм Зевара; найдите «zevar cerveaux assiste par ordinateur», чтобы найти эволюцию компьютера. Или попробуйте http://perinet.blogspirit.com/archive/2012/02/12/zevar-et- cointe.html пока он существует)

Для тех, кто действительно заинтересован (я видел некоторые предположения в комментариях): оригинал cpпервого Unix был около двух страниц кода на ассемблере (C появился позже). Соответствующая часть была:

sys open; name1: 0; 0   " Open the input file
spa
  jmp error         " File open error
lac o17         " Why load 15 (017) into AC?
sys creat; name2: 0     " Create the output file
spa
  jmp error         " File create error

(Итак, тяжело sys creat)

И пока мы работаем над этим: используется версия 2 Unix (фрагмент кода)

mode = buf[2] & 037;
if((fnew = creat(argv[2],mode)) < 0){
    stat(argv[2], buf);

что также трудно creatбез тестов и гарантий. Обратите внимание, что C-код для V2 Unix cpсоставляет менее 55 строк!

Ljm Dullaart
источник
5
Почти правильно, исключение - это « маленький пушистый » (существа из Альфа Центавра), а не « маленький пушистый »!
TripeHound
1
@TED: вполне возможно, что ранние версии cpпросто редактировали openпункт назначения O_CREAT | O_TRUNCи выполняли цикл read/ write; Конечно, у modern cpесть так много ручек, что он в основном должен пытаться statдобраться до места назначения заранее, и может легко сначала проверить существование (и делает это с cp -i/ cp -n), но если ожидания были установлены с помощью оригинальных cpинструментов, меняющих это поведение, изменяющих это поведение сломал бы существующие сценарии без необходимости. Это не то, что современные оболочки с aliasпросто не могут сделать cp -iпо умолчанию для интерактивного использования.
ShadowRanger
@ShadowRanger - Хммм. Вы совершенно правы, что я действительно понятия не имею, было ли это легко или сложно сделать. Комментарий удален.
TED
1
@ShadowRanger Да, но тогда это просто подталкивает тяжелый урок, пока он не
перейдет
1
@sourcejedi: весело! Не меняет мою основную теорию (что проще было просто безоговорочно открыть с усечением, и creatэто эквивалентно open+ O_CREAT | O_TRUNC), но отсутствие O_EXCLдействительно объясняет, почему было бы не так легко обрабатывать существующие файлы; попытка сделать это была бы по сути своей редкостью (вам, в основном, нужно open/ statпроверить существование, затем использовать creat, но на больших общих системах всегда возможно, когда вы дойдете creat, кто-то другой сделал файл, и теперь вы взорваны это все равно). Можно просто перезаписать безоговорочно.
ShadowRanger
19

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

Есть еще несколько гарантий:

  • GNU cpимеет -n| --no-clobberвариант
  • если вы скопируете несколько файлов в один, cpто пожалуетесь, что последний не является каталогом.
xenoid
источник
Это относится только к реализации конкретного поставщика, и вопрос был не о реализации конкретного поставщика.
Щили
10

Это "сделать одну вещь за один раз"?

Этот комментарий звучит как вопрос об общем принципе дизайна. Часто вопросы по этому поводу очень субъективны, и мы не можем дать правильный ответ. Имейте в виду, что в этом случае мы можем закрыть вопросы.

Иногда у нас есть объяснение первоначального выбора дизайна, потому что разработчики написали о них. Но у меня нет такого хорошего ответа на этот вопрос.

Почему cpтак устроено?

Проблема в том, что Unix старше 40 лет.

Если бы вы создавали новую систему сейчас, вы могли бы сделать другой выбор дизайна. Но изменение Unix сломало бы существующие сценарии, как упомянуто в других ответах.

Почему был cp разработан, чтобы молча перезаписать существующие файлы?

Краткий ответ: «Я не знаю» :-).

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

$ cat first.html > second.html

Эта команда также молча перезаписывает second.html.

Мне интересно подумать, как все эти программы могут быть переработаны. Это может потребовать дополнительной сложности.

Я думаю, что это часть объяснения: ранний Unix подчеркивал простые реализации . Более подробное объяснение этого см. В разделе «Чем хуже, тем лучше», ссылка на который приведена в конце этого ответа.

Вы можете изменить > second.htmlего, чтобы он остановился с ошибкой, если она second.htmlуже существует. Однако , как мы уже упоминали, иногда пользователь делает хочет заменить существующий файл. Например, она может создавать сложную команду, пытаясь несколько раз, пока она не сделает то, что она хочет.

Пользователь может запустить rm second.htmlпервым, если ему нужно. Это может быть хорошим компромиссом! У него есть некоторые возможные недостатки.

  1. Пользователь должен дважды ввести имя файла.
  2. Люди также получают много проблем с использованием rm. Поэтому я бы тоже хотел сделать его rmболее безопасным. Но как? Если мы rmпоказываем каждое имя файла и просим пользователя подтвердить, теперь она должна написать три строки команд вместо одной. Кроме того, если ей придется делать это слишком часто, она приобретет привычку и наберет «у», чтобы подтвердить, не задумываясь. Так что это может быть очень раздражающим и все же опасным.

В современной системе я рекомендую установить trashкоманду и использовать ее rmтам, где это возможно. Внедрение Trash Storage было отличной идеей, например, для однопользовательского графического ПК .

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

Обратите внимание, что в оригинальном Unix не было завершения табуляции , чтобы быстро заполнить имя файла для rmкоманды. (Кроме того, оригинальная оболочка Bourne не имеет истории команд, например, когда вы используете клавишу со стрелкой вверх bash).

При выводе на принтер вы бы использовали линейный редактор ed. Это сложнее освоить, чем визуальный текстовый редактор. Вы должны напечатать некоторые текущие строки, решить, как вы хотите их изменить, и ввести команду редактирования.

Использование > second.htmlнемного похоже на использование команды в редакторе строк. Эффект, который он оказывает, зависит от текущего состояния. (Если он second.htmlуже существует, его содержимое будет удалено). Если пользователь не уверен в текущем состоянии, он должен запускаться lsили ls second.htmlпервым.

«Простая реализация» как принцип проектирования

Существует популярная интерпретация Unix-дизайна, которая начинается с:

Дизайн должен быть простым, как по реализации, так и по интерфейсу. Для реализации важнее быть простым, чем интерфейс. Простота является наиболее важным фактором в дизайне.

...

Габриэль утверждал, что «Хуже лучше» создает более успешное программное обеспечение, чем подход MIT: пока первоначальная программа в основном хороша, ее реализация займет гораздо меньше времени и усилий, и ее будет легче адаптировать к новым ситуациям. Например, перенос программного обеспечения на новые машины становится намного проще. Таким образом, его использование будет быстро распространяться задолго до того, как [лучшая] программа получит шанс для разработки и развертывания (преимущество первопроходца).

https://en.wikipedia.org/wiki/Worse_is_better

sourcejedi
источник
Почему цель перезаписывается на cp«проблему»? Наличие интерактивного запроса разрешения или неудачи может быть такой же большой «проблемой», как эта.
Кусалананда
вау, спасибо Дополните руководство: 1) Напишите программы, которые делают одно и делают это хорошо. 2) Доверьтесь программисту.
Алгебра
2
@ Kusalananda потеря данных является проблемой. Я лично заинтересован в снижении риска потери данных. Есть разные подходы к этому. Сказать, что это проблема, не означает, что альтернативы также не имеют проблем.
sourcejedi
1
@riderdragon Программы, написанные на языке C, часто могут очень неожиданно выходить из строя, потому что C доверяет программисту. Но программисты просто не так надежны. Мы должны написать очень продвинутые инструменты, такие как valgrind , которые необходимы, чтобы попытаться найти ошибки, которые делают программисты. Я думаю, что важно иметь такие языки программирования, как Rust, Python или C #, которые пытаются обеспечить «безопасность памяти», не доверяя программисту. (Язык C был создан одним из авторов UNIX, чтобы писать UNIX на переносимом языке).
sourcejedi
1
Еще лучше, это cat first.html second.html > first.htmlдаст результат, first.htmlбудучи перезаписанным только с содержимым second.html. Оригинальное содержание теряется на все времена.
doneal24
9

Дизайн "cp" восходит к оригинальному дизайну Unix. На самом деле, за дизайном Unix стояла последовательная философия, которая была чуть менее шутливо названа « Хуже-лучше» * .

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

  • Простота - дизайн должен быть простым, как по реализации, так и по интерфейсу. Для реализации важнее быть простым, чем интерфейс . Простота является наиболее важным фактором в дизайне.

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

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

  • Полнота - дизайн должен охватывать столько важных ситуаций, сколько это практически возможно. Все разумно ожидаемые случаи должны быть покрыты. Полнота может быть принесена в жертву в пользу любого другого качества. Фактически, полнота должна быть принесена в жертву всякий раз, когда простота реализации находится под угрозой. Последовательность может быть принесена в жертву для достижения полноты, если простота сохраняется; Особенно бесполезна последовательность интерфейса.

( акцент мой )

Помня, что это был 1970 год, вариант использования «Я хочу скопировать этот файл, только если он еще не существует» был бы довольно редким случаем для кого-то, кто выполняет копирование. Если это то, что вы хотели, вы вполне могли бы проверить перед копированием, и это можно даже записать в сценарии.

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

Еще одно преимущество философии «хуже - лучше» заключается в том, что программист вынужден жертвовать некоторой безопасностью, удобством и хлопотами, чтобы получить хорошую производительность и скромное использование ресурсов. Программы, написанные с использованием подхода Нью-Джерси, будут хорошо работать как на небольших, так и на больших машинах, а код будет переносимым, поскольку он написан поверх вируса.

Важно помнить, что первоначальный вирус должен быть в основном хорошим. Если это так, то распространение вируса гарантировано, пока оно переносимо. Как только вирус распространился, возникнет необходимость в его улучшении, возможно, за счет увеличения его функциональности ближе к 90%, но пользователи уже были готовы принять худшее, чем нужно. Таким образом, программное обеспечение «хуже-лучше» сначала получит признание, второе заставит пользователей ожидать меньшего, а третье будет улучшено до уровня, который является почти правильным.

* - или то, что автор, но никто другой, назвал «подход Нью-Джерси» .

ТЕД
источник
1
Это правильный ответ.
tchrist
+1, но я думаю, что это поможет иметь конкретный пример. Когда вы устанавливаете новую версию программы, которую вы редактировали и перекомпилировали (и, возможно, протестировали :-)), вы намеренно хотите перезаписать старую версию программы. (И вы , вероятно , хотите , подобное поведение с компилятором. Так рано UNIX имеет только creat()против open(). open()Не может создать файл , если он не существует. Он принимает только 0/1/2 для чтения / записи / другое. Это не займет O_CREAT, и нет O_EXCL).
sourcejedi
@sourcejedi - Извините, но, как разработчик программного обеспечения, я, честно говоря, не могу придумать другой сценарий, кроме того, где я буду делать копию. :-)
TED
@TED ​​Извините, я имею в виду, что я предлагаю этот пример в качестве одного из не редких случаев, когда вы определенно хотите перезаписать, по сравнению со сравнением в вопросе, где, возможно, вы этого не сделали.
sourcejedi
0

Основная причина в том, что графический интерфейс по определению является интерактивным, в то время как двоичный код /bin/cp- это просто программа, которую можно вызывать из любого места, например из вашего графического интерфейса ;-). Могу поспорить, что даже сегодня подавляющее большинство звонков /bin/cpбудет поступать не с реального терминала, где пользователь вводит команду оболочки, а с HTTP-сервера, почтовой системы или NAS. Встроенная защита от ошибок пользователя имеет смысл в интерактивной среде; меньше так в простом бинарном файле. Например, ваш GUI, скорее всего, будет вызывать /bin/cpв фоновом режиме для выполнения реальных операций, и ему придется иметь дело с вопросами безопасности на стандартном out, даже если он только что спросил пользователя!

Обратите внимание, что с первого дня было почти тривиально написать безопасную оболочку, /bin/cpесли это необходимо. Философия * nix заключается в предоставлении пользователям простых строительных блоков: один из них /bin/cp.

Питер - Восстановить Монику
источник