Мені не було відомо про нагороду до сьогодні, коли якийсь новичок намагався прив’язати УУПЦ до мене за однією з моїх відповідей. Це було cat file.txt | grep foo | cut ... | cut ...
. Я віддав йому частину своєї думки, і лише після цього відвідав посилання, він дав мені посилання на походження нагороди та практику цього робити. Подальший пошук привів мене до цього питання. На жаль, незважаючи на свідомий розгляд, жодна з відповідей не включила мого обґрунтування.
Я не мав на увазі захищати його, коли виховував його. Зрештою, в мої молодші роки я написав би команду, grep foo file.txt | cut ... | cut ...
тому що, коли ви робите часті сингли grep
, ви дізнаєтесь про розміщення аргументу файлів, і ви знаєте, що перший - це шаблон, а пізніше - це назви файлів.
Це був свідомий вибір, коли я відповів на запитання з cat
префіксом частково через причину "гарного смаку" (словами Лінуса Торвальдса), але головним чином з переконливої причини функціонування.
Остання причина важливіша, тому я викладу її першою. Коли я пропоную трубопровід як рішення, я очікую його повторного використання. Цілком ймовірно, що трубопровід буде доданий в кінці або зрощений в інший трубопровід. У такому випадку аргумент файлу grep прискорює повторне використання, і цілком можливо, зробіть це мовчки без повідомлення про помилку, якщо аргумент файлу існує. І. е. grep foo xyz | grep bar xyz | wc
дасть вам кількість рядків, що xyz
містять, bar
поки ви очікуєте кількість рядків, що містять і foo
і, і bar
. Необхідність змінити аргументи команді в конвеєрі перед її використанням схильна до помилок. Додайте до нього можливість мовчазних збоїв, і це стає особливо підступною практикою.
Колишня причина також не є важливою, оскільки багато "гарного смаку" просто інтуїтивне підсвідоме обгрунтування таких речей, як мовчазні невдачі, про які ви не можете подумати в той момент, коли хтось, хто потребує освіти, каже "але це не так той кіт марний ".
Однак я спробую також усвідомити колишню причину "доброго смаку", яку я згадав. Ця причина пов'язана з ортогональним дизайнерським духом Unix. grep
не робить cut
і ls
не робить grep
. Тому як мінімум grep foo file1 file2 file3
йде проти дизайнерського духу. Ортогональний спосіб це зробити cat file1 file2 file3 | grep foo
. Тепер grep foo file1
це лише особливий випадок grep foo file1 file2 file3
, і якщо ви не ставитесь до цього так само, ви принаймні використовуєте цикли мозку годинника, намагаючись уникнути марної нагороди котам.
Це призводить нас до аргументу, який grep foo file1 file2 file3
об'єднує, і cat
об'єднує, так що це належить, cat file1 file2 file3
але тому cat
, що не є об'єднувальним, cat file1 | grep foo
тому ми порушуємо дух cat
і Всемогутнього Unix. Ну, якщо б це було так, то Unix потрібна була б інша команда, щоб прочитати вихід одного файлу і виплюнути його на stdout (а не пропагувати його чи нічого просто чистого коса для stdout). Таким чином, у вас виникла б ситуація, коли ви говорите cat file1 file2
або говорите, dog file1
і сумлінно пам’ятаєте, щоб уникнути cat file1
отримання нагороди, а також уникати, dog file1 file2
оскільки, сподіваємось, дизайн dog
може призвести до помилки, якщо вказано кілька файлів.
Сподіваємось, що в цей момент ви співчуваєте дизайнерам Unix за те, що вони не включають окрему команду, щоб виплюнути файл в stdout, а також іменувати cat
concatenate, а не давати йому якесь інше ім'я. <edit>
є така собака, нещасний <
оператор. Прикро, що його розміщення в кінці трубопроводу запобігає легкому компостуванню. Не існує синтаксичного чи естетично чистого способу розмістити його на початку. Також прикро не бути достатньо загальним, тому ви починаєте з собаки, а просто додаєте інше ім'я файлу, якщо ви також хочете, щоб воно було оброблене після попереднього. (З >
іншого боку, це не наполовину погано. Він має майже ідеальне розміщення в кінці. Зазвичай це не частина багаторазового використання трубопроводу, і, відповідно, його виділяють символічно.)</edit>
Наступне питання: чому важливо мати команди, які просто плюють на файл або об'єднання декількох файлів у stdout, без подальшої обробки? Одна з причин - уникати наявності кожної однієї команди Unix, яка працює на стандартному вході, щоб знати, як розібрати принаймні один аргумент файлу командного рядка та використовувати його як вхідний файл, якщо він існує. Друга причина - уникати пам’яті користувачам: (a) куди йдуть аргументи імені файлів; та (b) уникати тихої помилки в трубопроводі, як згадувалося вище.
Це підводить нас до того, чому у grep
них є додаткова логіка. Обґрунтування полягає в тому, щоб дозволити вільне користування командами, які часто використовуються та окремо (а не як конвеєр). Це невеликий компроміс ортогональності для значного збільшення зручності використання. Не всі команди повинні бути розроблені таким чином, і команди, які не часто використовуються, повинні повністю уникати зайвої логіки аргументів файлів (пам'ятайте, додаткова логіка призводить до зайвої крихкості (можливість помилки)). Виняток - дозволити аргументи файлів, як у випадку з grep
. (до речі, зауважте, що ls
є зовсім інша причина не просто приймати, але в значній мірі вимагати аргументів файлів)
Нарешті, що можна було б зробити краще, якщо такі виняткові команди, як grep
(але не обов'язково ls
), створюють помилку, якщо стандартний вхід є. Це розумно, тому що команди містять логіку, яка порушує ортогональний дух всемогутнього Unix для зручності користувача. Для подальшої зручності користувача, тобто для запобігання страждань, спричинених тихим збоєм, такі команди не повинні вагатися з порушенням власного порушення, попереджаючи користувача, якщо є можливість беззвучного відмови.