Чому `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
Я гадаю, що лише дизайнери з основних програм можуть по-справжньому відповісти на питання, але це саме так, як це працює. Зазвичай програми створюються, якщо користувач дійсно означає, що вони роблять, і мінімізувати додаткові підказки. Якщо ви хочете змінити поведінку, псевдонім 'cp' на 'cp -i' або 'cp -n'.
kevlinux

8
@kevlinux Розробники coreutils просто реалізують стандарт POSIX.
Kusalananda

17
Тому що тоді, коли він був розроблений, люди хотіли бути максимально стислими з тим, що роблять (отже, не копіюючи cp) і знали, що вони роблять, і коли вони робили помилки, вони не намагалися звинувачувати інструменти. Тоді це були зовсім інші люди. Це як запитати, чому скальпель для серцевого хірурга теж може порізати руки.
ПлазмаHH

4
Unix був розроблений і для комп'ютерних експертів, з припущенням, що користувач знає, що робить. ОС може робити саме те, що користувач сказав йому, якщо це можливо, не тримаючи користувача за руку і не вимагаючи нескінченних підтверджень. Якщо операція щось перекреслила, передбачалося, що саме цього бажає користувач. Також пам’ятайте, що це було на початку 1970-х - до MS-DOS, Windows та домашніх комп’ютерів - керуючи та тримаючи руку користувача на кожному кроці, ще не було поширеним. Крім того, якщо термінали, оброблені телетайпами, як термінали, запити про підтвердження завжди було б занадто громіздким.
Баард Копперуд

10
Чи не псевдонім cpдля cp -iабо подібне , тому що ви будете звикати до того , підстраховка, що робить системи , в яких він не доступний (більшість з них) , що набагато більш ризикованими. Краще навчити себе рутинно cp -iтощо, якщо саме так ви віддаєте перевагу.
Рейд

Відповіді:


52

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

  1. Якщо файл source_file має звичайний файл типу, слід зробити наступні кроки:

    3.а. Поведінка не визначено, якщо 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 (), визначеної в томі системних інтерфейсів POSIX.1-2017, викликаному за допомогою dest_file як аргумент шляху. Якщо ця спроба успішна, cp продовжуватиметься з кроку 3b.

Коли специфікація POSIX була написана, вже існувала велика кількість сценаріїв, з вбудованим припущенням для поведінки перезапису за замовчуванням. Багато з цих сценаріїв були розроблені для роботи без прямої присутності користувача, наприклад, як завдання cron або інші фонові завдання. Зміна поведінки може порушити їх. Перегляд та змінення їх усіх, щоб додати параметр для примусового перезапису, де це було потрібно, мабуть, вважали величезною задачею з мінімальними перевагами.

Також командний рядок Unix завжди був розроблений, щоб дозволити досвідченому користувачеві ефективно працювати навіть за рахунок жорсткої кривої навчання для початківця. Коли користувач вводить команду, комп'ютер повинен сподіватися, що користувач дійсно це означає, без жодного другого відгадування; це відповідальність користувача бути обережним з потенційно руйнівними командами.

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

Коли писався стандарт POSIX, прецедент був твердо встановлений, і автори стандарту добре розуміли чесноти не порушувати зворотній сумісності .

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

Але якщо ви зробите це, то виявите, що створюєте небезпеку для себе. Якщо cpкоманда поводиться в один спосіб при інтерактивному використанні, а інший при виклику зі сценарію, ви можете не пам'ятати, що різниця існує. В іншій системі ви можете виявитись необережними, оскільки ви звикли до попереджень та підказок у власній системі.

Якщо поведінка в сценаріях все ще буде відповідати стандарту POSIX, ви, швидше за все, звикнете до підказок в інтерактивному використанні, тоді напишіть сценарій, який робить деяке масове копіювання - і тоді виявите, що ви знову ненавмисно щось перезаписали.

Якщо ви також застосуєте запит в сценаріях, що робитиме команда, коли запускається в контексті, в якому немає користувача, наприклад фонових процесів чи завдань cron? Чи буде сценарій висіти, скасувати чи перезаписати?

Повішування або переривання вагітності означає, що завдання, яке повинно було виконатися автоматично, не буде виконано. Неперезаписування іноді також може спричинити проблеми самі по собі: наприклад, це може спричинити обробку старих даних двічі іншою системою замість того, щоб замінюватись оновленими даними.

Значна частина потужності командного рядка походить від того, що, коли ви знаєте, як зробити щось у командному рядку, ви неявно також знатимете, як зробити це автоматично шляхом написання сценарію . Але це справедливо лише в тому випадку, якщо команди, які ви використовуєте інтерактивно, також працюють точно так само, коли викликаються в контексті сценарію. Будь-які суттєві відмінності в поведінці між інтерактивним та скриптовим використанням створюють свого роду когнітивний дисонанс, який дратує споживача енергії.


54
"Чому це працює так?" "Тому що стандарт говорить так". "Чому стандарт так говорить?" "Тому що це вже працювало сподобалось".
Батіст Кандильєр

16
Останній абзац - реальна причина.
Діалогове вікно

@BaptisteCandellier - Погоджено. Це як кінцева причина там, але дошкульно просто поза досяжністю цієї відповіді.
ТЕД

2
Цей останній абзац є rm -rfнастільки ефективним, навіть якщо ви насправді не хотіли запустити його у своєму домашньому каталозі ...
Макс Вернон,

2
@TED Забавно , як ніхто ніколи не згадує , як скасування зв'язків (2) SYSCALL також невиконання "запитати « Мати, може чи я? » Для підтвердження коли ці обговорення знову вічного ззаду їх ласощі голови. :)
tchrist

20

cpпоходить від початку Unix. Це було там ще до того, як був написаний стандарт Posix. Справді: Posix просто формалізував існуючу поведінку cpв цьому плані.

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

Тож у ті часи ви мусили знати, що ви робите, бо комп'ютер просто не мав сили перешкодити вам робити все, про що ви пошкодували пізніше.

(Є приємний мультфільм від Zevar; шукайте "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 рядків!


5
Майже правильно, excepr це " маленькі пухнасті " (істоти з Альфи Центавра), а не " пухнасті маленькі "!
TripeHound

1
@ TED: Цілком можливі ранні версії cpщойно openредагування пункту призначення O_CREAT | O_TRUNCта виконання циклу read/ write; звичайно, у сучасних cpє так багато ручок, що в основному доводиться statзаздалегідь спробувати до пункту призначення, і можна було б спочатку перевірити наявність на першому місці (і це робиться з cp -i/ cp -n), але якщо очікування були встановлені від оригінальних cpінструментів для голих кісток , змінивши таку поведінку зламає існуючі сценарії без потреби. Це не так, як сучасні оболонки, з якими aliasне може просто зробити cp -iза замовчуванням інтерактивне використання.
ShadowRanger

@ShadowRanger - Хммм. Ви абсолютно праві, що я насправді не маю уявлення, чи це було легко чи важко зробити. Коментар видалено.
ТЕД

1
@ShadowRanger Так, але тоді це просто підштовхує важке заняття вниз по дорозі, поки воно не стане виробничою системою ...
chrylis

1
@sourcejedi: Весело! Це не змінює мою основну теорію (що було легше просто беззастережно відкрити з усіченням, і creatбуває, що це еквівалентно open+ O_CREAT | O_TRUNC), але відсутність O_EXCLпояснює, чому не було б так просто обробляти існуючі файли; спроба зробити це було б за своєю суттю нестримним (в основному вам доведеться open/ statперевірити існування, а потім використовувати creat, але у великих спільних системах це завжди можливо до моменту, коли вам належить creat, хтось інший створив файл, і тепер ви подули це все одно). Може також просто перезаписати.
ShadowRanger

19

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

Є ще кілька гарантій:

  • GNU cpмає -n| --no-clobberваріант
  • якщо ви скопіюєте кілька файлів в один, cpто скаржитеся, що останній не є каталогом.

Це стосується лише конкретної реалізації постачальника, і питання не стосувалося конкретної реалізації цього постачальника.
schily

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показати кожне ім’я файлу і попросимо користувача підтвердити, тепер він повинен написати три рядки команд замість одного. Крім того, якщо їй доводиться робити це занадто часто, вона ввійде в звичку і надумайте "y" для підтвердження, не замислюючись. Тож це може бути дуже дратівливим, а ще може бути небезпечним.

У сучасній системі я рекомендую встановити trashкоманду та використовувати її замість того, rmде це можливо. Впровадження сховища Trash було чудовою ідеєю, наприклад, для графічного ПК для одного користувача .

Я думаю, що також важливо розуміти обмеження оригінального обладнання Unix - обмежена оперативна пам’ять та дисковий простір, вихід, який відображається на повільних принтерах , а також програмне забезпечення для системи та розробки.

Зауважте, що оригінальний Unix не завершив вкладку , щоб швидко заповнити ім'я файлу для rmкоманди. (Також оригінальна оболонка Bourne не має історії команд, наприклад, наприклад, коли ви використовуєте клавішу зі стрілкою вгору bash).

З виходом принтера, можна використовувати лінії на основі редактора ed. Це важче засвоїти, ніж візуальний редактор тексту. Вам потрібно роздрукувати деякі поточні рядки, вирішити, як ви хочете їх змінити, і ввести команду редагування.

Використання > second.htmlтрохи схоже на використання команди в редакторі рядків. Ефект, який він має, залежить від поточного стану. (Якщо вона second.htmlвже існує, її вміст буде відкинуто). Якщо користувач не впевнений у поточному стані, очікується, що він запуститься lsабо ls second.htmlперший.

«Проста реалізація» як принцип дизайну

Існує популярна інтерпретація дизайну Unix, яка починається:

Дизайн повинен бути простим як в реалізації, так і в інтерфейсі. Важливо, щоб реалізація була простою, ніж інтерфейс. Простота - це найважливіша увага в дизайні.

...

Габріель стверджував, що "Гірше краще" виробляє більш успішне програмне забезпечення, ніж підхід MIT: Поки початкова програма в основному хороша, для її впровадження спочатку знадобиться набагато менше часу і сил, і буде легше адаптуватися до нових ситуацій. Наприклад, перенесення програмного забезпечення на нові машини стає набагато простішим. Таким чином, його використання буде швидко поширюватися, задовго до того, як [краща] програма матиме шанс бути розробленою та впровадженою (перевага першої особи).

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


Чому перезапис цілі cp"проблемою"? В інтерактивному режимі просити дозволу, або це може бути настільки ж великою «проблемою».
Kusalananda

вау, дякую доповнити керівництво: 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, який трохи менше був , що напівжартома називають як гірше-це-Better * .

Основна ідея полягає в тому, що збереження коду простим - насправді важливіше розгляд дизайну, що мати ідеальний інтерфейс або "робити правильну річ".

  • Простота - дизайн повинен бути простим, як в реалізації, так і в інтерфейсі. Важливо, щоб реалізація була простою, ніж інтерфейс . Простота - це найважливіша увага в дизайні.

  • Правильність - дизайн повинен бути правильним у всіх спостережуваних аспектах. Трохи краще бути простим, ніж правильним.

  • Консистенція - дизайн не повинен бути надмірно непослідовним. Послідовність може бути принесена в жертву для простоти в деяких випадках, але краще відмовитися від тих частин конструкції, які стосуються менш поширених обставин, ніж вводити або складність в реалізації, або невідповідність.

  • Повнота - дизайн повинен охоплювати стільки важливих ситуацій, скільки є практичним. Усі обґрунтовано очікувані справи повинні бути висвітлені. Повноту можна принести в жертву на користь будь-якої іншої якості. Фактично, повнота повинна бути принесена у жертву, коли піддається небезпеці простота впровадження. Послідовність може бути принесена в жертву для досягнення повноти, якщо простота зберігається; Особливо марною є послідовність інтерфейсу.

( наголос мій )

Пам'ятаючи, що це був 1970 рік, випадок використання "Я хочу скопіювати цей файл, лише якщо його вже не існує" був би досить рідкісним випадком використання для тих, хто виконує копію. Якщо це те, що ви хотіли, ви зможете перевірити його, перш ніж копіювати, і це навіть можна прописати.

Щодо того, чому ОС з таким дизайнерським підходом виявилася тією, яка перемогла над усіма іншими побудованими ОС у той час, автор реферату також мав теорію для цього.

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

Важливо пам’ятати, що початковий вірус повинен бути в основному хорошим. Якщо так, вірусне розповсюдження гарантується до тих пір, поки воно є портативним. Як тільки вірус пошириться, з’явиться тиск для його вдосконалення, можливо, збільшивши його функціональність ближче до 90%, але користувачі вже були обумовлені сприйняти гірше за потрібне. Тому програмне забезпечення, що гірше, краще - перше отримає прийняття, друге спричинить його очікування користувачів менше, а третє буде покращене до рівня, що є майже правильним.

* - або те, що автор, але ніхто інший, назвав «Підхід Нью-Джерсі» .


1
Це правильна відповідь.
tchrist

+1, але я думаю, це допомогло б мати конкретний приклад. Встановлюючи нову версію програми, яку ви відредагували та перекомпілювали (і можливо тестували :-), ви свідомо хочете перезаписати стару версію програми. (І ви , ймовірно , хочете , подібна поведінка з компілятором. Так рано UNIX має тільки creat()проти open(). open()Не може створити файл , якщо він не існує. Він приймає лише 0/1/2 для читання / запису / інше. Це не займе O_CREAT, і немає O_EXCL).
sourcejedi

@sourcejedi - Вибачте, але, як сам розробник програмного забезпечення, я, чесно кажучи, не можу придумати інший сценарій, ніж той, де я б робив копію. :-)
ТЕД

@ TED Вибачте, я маю на увазі, що я пропоную цей приклад, як один з не рідкісних випадків, коли ви, безумовно, хочете перезаписати, порівняння у питанні, де, можливо, ви цього не зробили.
sourcejedi

0

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

Зауважте, що з першого дня, близького до тривіального, потрібно писати безпечну обгортку навколо, /bin/cpякщо так хочеться. Філософія * nix полягає у наданні простих будівельних блоків для користувачів: з них /bin/cpодна.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.