web-dev-qa-db-ja.com

なぜ `cp`は既存のファイルを黙って上書きするように設計されたのですか?

次のコマンドを使用してcpをテストしました。

$ ls
first.html   second.html  third.html

$ cat first.html
first

$ cat second.html
second

$ cat third.html
third

次に、first.htmlsecond.htmlにコピーします。

$ cp first.html second.html

$ cat second.html
first

ファイルsecond.htmlはエラーなしで警告なしに上書きされます。ただし、同じ名前のファイルをドラッグアンドドロップしてデスクトップGUIで実行すると、自動的にfirst1.htmlのサフィックスが付きます。これにより、既存のファイルを誤って上書きすることを防ぎます。

サイレントでファイルを上書きする代わりに、なぜこのパターンをcp実行しないのですか?

29
Calculus

cpのデフォルトの上書き動作はPOSIXで指定されています。

  1. Source_fileのタイプが通常ファイルの場合、次の手順を実行する必要があります。

    3.a. dest_fileが存在し、前のステップで書き込まれた場合の動作は規定されていません。それ以外の場合、dest_fileが存在する場合は、次の手順を実行する必要があります。

    3.a.i. -iオプションが有効な場合、cpユーティリティは標準エラーにプロンプ​​トを書き込み、標準入力から行を読み取ります。応答が肯定的でない場合、cpはsource_fileに対してこれ以上何もせず、残りのファイルに進みます。

    3.a.ii。 dest_fileのファイル記述子は、パス引数としてdest_fileを使用して呼び出されたPOSIX.1-2017のシステムインターフェースボリュームで定義されたopen()関数と同等のアクションを実行し、ビット単位の包含OR oflag引数としてO_WRONLYおよびO_TRUNCの。

    3.a.iii。ファイル記述子を取得する試みが失敗し、-fオプションが有効な場合、cpは、dest_fileを使用して呼び出されるPOSIX.1-2017のシステムインターフェースボリュームで定義されたunlink()関数と同等のアクションを実行して、ファイルを削除しようとします。パス引数として。この試みが成功した場合、cpはステップ3bを続行します。

POSIX仕様が作成されたとき、デフォルトの上書き動作についての組み込みの仮定を使用して、すでに多数のスクリプトが存在していました。これらのスクリプトの多くは、直接的なユーザーの存在なしで実行するように設計されています。 cronジョブまたは他のバックグラウンドタスクとして。動作を変更すると、それらが壊れます。それらをすべて確認および変更して、必要な場所に上書きを強制するオプションを追加することは、最小限のメリットしか得られない大きなタスクと考えられていました。

また、Unixコマンドラインは、初心者にとっては難しい学習曲線を犠牲にしても、経験豊富なユーザーが効率的に作業できるように常に設計されています。ユーザーがコマンドを入力すると、コンピューターは、ユーザーが実際にそれを意味することを期待し、二次的な推測をしません。潜在的に破壊的なコマンドに注意するのはユーザーの責任です。

オリジナルのUnixが開発されたとき、システムは、警告やプロンプトを上書きする現代のコンピューターと比較して、メモリと大容量ストレージがほとんどないため、おそらく無駄で不要な贅沢と見なされていました。

POSIX標準が作成されたとき、前例はしっかりと確立されており、標準の作成者はの下位互換性を壊さないことの利点をよく知っていました

さらに、他のユーザーが説明したように、シェルエイリアスを使用するか、置換cpコマンドを作成し、$PATHを変更して標準の前に代替を見つけることで、すべてのユーザーが自分でこれらの機能を追加/有効化できますシステムコマンド、および必要に応じてその方法でセーフティネットを取得します。

しかし、そうすると、自分自身に危険をもたらしていることに気づくでしょう。 cpコマンドが対話的に使用されたときの動作とスクリプトから呼び出されたときの動作が異なる場合、違いがあることを覚えていない可能性があります。別のシステムでは、自分のシステムの警告とプロンプトに慣れるので、不注意になる可能性があります。

スクリプトの動作が引き続きPOSIX標準に一致する場合、インタラクティブな使用でプロンプトに慣れ、大量のコピーを実行するスクリプトを記述します。その後、誤って何かが上書きされていることに気づくでしょう。

スクリプトでもプロンプトを強制する場合、ユーザーがいないコンテキストでコマンドを実行するとどうなりますか。バックグラウンドプロセスまたはcronジョブ?スクリプトはハングアップ、中止、または上書きされますか?

ハングまたは中止とは、自動的に実行されるはずのタスクが実行されないことを意味します。上書きしないと、それ自体で問題が発生する場合もあります。たとえば、古いデータが最新のデータで置き換えられるのではなく、別のシステムで2回処理される可能性があります。

コマンドラインの機能の大部分は、コマンドラインで何かを行う方法を知ったら、スクリプトによって自動的にそれを自動的に実行する方法も暗黙的に知っているという事実に由来します。ただし、対話的に使用するコマンドがスクリプトコンテキストで呼び出された場合もまったく同じように機能する場合にのみ当てはまります。インタラクティブな使用とスクリプトを使用した場合の動作に大きな違いがあると、一種の認知的不協和音が発生します。これは、パワーユーザーを悩ませます。

51
telcoM

cpは、Unixの最初から使用されています。それは、Posix標準が書かれるずっと前からありました。実際、Posixはこの点に関してcpの既存の動作を形式化しました。

私たちはエポック(1970-01-01)について話している。男性が本物の男性であり、女性が本物の女性であり、毛皮で覆われた小さな生き物だった...(私は余談ですが)。当時は、コードを追加することでプログラムが大きくなりました。 Unixを実行した最初のコンピューターがPDP-7(144KB RAMにアップグレード可能!)だったので、それはそれで問題でした。そのため、安全機能がなく、物事は小さく、効率的でした。

そのため、当時はコンピューターに自分が後で後悔することを行うのを防ぐ力がなかったため、自分が何をしていたかを知る必要がありました。

(Zevarによる素敵な漫画があります。「zevar cerveaux Asse par ordinateur」を検索して、コンピューターの進化を見つけてください。または、試してください http://a54.idata.over-blog.com/2/07/ 74/62/dessins-et-bd/le-CAO-de-Zevar --- reduc.jpg 存在する限り)

本当に興味のある人のために(コメントでいくつかの憶測を見ました):最初のUnixの元のcpは、約2ページのアセンブラコードでした(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

そして、私たちがそれに向かっている間:Unixのバージョン2が使用されました(コードスニペット)

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

これもまた、テストや保護手段なしでは難しいcreatです。 cpのV2 UnixのCコードは55行未満です。

19
Ljm Dullaart

これらのコマンドはスクリプトで使用することも意図されているため、おそらく人間の監視なしで実行されます。また、ターゲットを実際に上書きしたい場合がたくさんあるためです(Linuxシェルの哲学は、人間が何を知っているかということです) s /彼はやっている)

まだいくつかの保護手段があります:

  • GNU cpには-n | --no-clobberオプション
  • 複数のファイルを1つのファイルにコピーすると、cpは最後のファイルがディレクトリではないことを報告します。
17
xenoid

「一度に一つのこと」ですか?

このコメントは、一般的な設計原則についての質問のように聞こえます。多くの場合、これらに関する質問は非常に主観的であり、適切な回答を書くことができません。この場合、質問をクローズする可能性があることに注意してください。

開発者がそれらについて書いたため、元の設計の選択について説明がある場合があります。しかし、私はこの質問に対するそのような素晴らしい答えはありません。

なぜcpはこのように設計されているのですか?

問題は、Unixが40年以上古いことです。

今、新しいシステムを作成している場合は、別の設計を選択する可能性があります。しかし、他の回答で述べたように、Unixを変更すると、既存のスクリプトが破損します。

なぜwascpは既存のファイルを静かに上書きするように設計されているのですか?

短い答えは「わからない」です:-)。

cpは1つの問題にすぎないことを理解してください。元のコマンドプログラムはいずれも、ファイルの上書きまたは削除から保護されていないと思います。シェルには、出力をリダイレクトするときに同様の問題があります。

$ cat first.html > second.html

このコマンドは、サイレントにsecond.htmlも上書きします。

これらすべてのプログラムをどのように再設計できるかを考えてみたいと思います。それはいくつかの追加の複雑さを必要とするかもしれません。

これは説明の一部だと思います:初期のUNIXはシンプルな実装を強調しました詳細な説明この回答の最後にリンクされている「悪い方が良い」を参照してください。

> second.htmlがすでに存在する場合、second.htmlを変更してエラーで停止することができます。ただし、前述したように、ユーザーが既存のファイルを置き換えたい場合があります。たとえば、彼女は複雑なコマンドを作成していて、それが自分のやりたいことを実行するまで数回試行している場合があります。

ユーザーは必要に応じて、最初にrm second.htmlを実行できます。これは良い妥協かもしれません!それはそれ自身のいくつかの可能な欠点があります。

  1. ユーザーはファイル名を2回入力する必要があります。
  2. rmを使用すると、多くの問題が発生します。ですから、rmもより安全なものにしたいと思います。しかし、どうやって? rmに各ファイル名を表示させ、ユーザーに確認を求める場合、ユーザーは1行ではなく3行のコマンド行を記述する必要があります。また、これを頻繁に行う必要がある場合は、習慣になり、「y」と入力して何も考えずに確認します。したがって、それは非常に迷惑であり、それでも危険である可能性があります

最近のシステムでは trashコマンドをインストールする を推奨し、可能であればrmの代わりにそれを使用します。ゴミ箱ストレージの導入は素晴らしいアイデアでした。 シングルユーザーのグラフィカルPCの場合

元のUnixハードウェアの制限を理解することも重要だと思います-制限付きRAMとディスク容量、遅い プリンタ に表示される出力、およびシステムと開発ソフトウェア。

rmコマンドのファイル名をすばやく入力するために、元のUnixには タブ補完 がなかったことに注意してください。 (また、元のBourne Shellにはコマンド履歴がありません。たとえば、bashで上矢印キーを使用する場合など)。

プリンター出力では、行ベースのエディターedを使用します。これは、ビジュアルテキストエディタよりも学ぶのが難しいです。現在の行を印刷し、変更方法を決定して、編集コマンドを入力する必要があります。

> second.htmlの使用は、ラインエディタでコマンドを使用するのと少し似ています。その影響は、現在の状態によって異なります。 (second.htmlがすでに存在する場合、その内容は破棄されます)。ユーザーが現在の状態がわからない場合は、最初にlsまたはls second.htmlを実行する必要があります。

設計原理としての「シンプルな実装」

Unixデザインの一般的な解釈があります。

設計は、実装とインターフェースの両方において単純でなければなりません。実装はインターフェースよりもシンプルであることが重要です。シンプルさは、設計において最も重要な考慮事項です。

...

ガブリエルは、「悪いほうが良い」はMITアプローチよりも優れたソフトウェアを生み出した:初期のプログラムが基本的に良好である限り、最初に実装するのにはるかに少ない時間と労力で済み、新しい状況への適応が容易になります。たとえば、この方法でソフトウェアを新しいマシンに移植するのがはるかに簡単になります。したがって、[より良い]プログラムが開発および展開される機会が得られるずっと前に、その使用は急速に広がります(先駆者の利点) )。

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

9
sourcejedi

「cp」の設計は、Unixの元の設計に戻ります。実際、Unix設計の背後にある一貫した哲学がありました。これは、冗談で Worse-is-Better と呼ばれるよりもわずかに少なくなっています。*

基本的な考え方は、コードをシンプルに保つことは、完璧なインターフェースを持つこと、または「正しいことを行う」ことよりも、実際にはより重要な設計上の考慮事項であるということです。

  • シンプルさ-デザインは、実装とインターフェースの両方でシンプルでなければなりません。 インターフェースよりも実装が単純であることが重要です。シンプルさは、設計において最も重要な考慮事項です。

  • 正確さ-設計は、すべての観察可能な面で正確でなければなりません。正しいというよりは、単純である方が少し良いです。

  • 一貫性-設計に過度の一貫性があってはなりません。場合によっては、単純化のために一貫性を犠牲にすることもできますが、あまり一般的でない状況に対応する設計の部分を削除して、実装を導入するよりも適切です複雑さまたは矛盾。

  • 完全性-設計は、実用的な限り多くの重要な状況をカバーする必要があります。合理的に予想されるケースはすべてカバーする必要があります。他の品質を優先して、完全性を犠牲にすることができます。実際、実装の簡素化が危うくなる場合は常に、完全性を犠牲にする必要があります。単純性が保たれている場合、整合性を犠牲にして完全性を達成できます。特に価値がないのは、インターフェースの一貫性です。

強調鉱山

これが1970年であったことを思い出して、「このファイルがまだ存在しない場合にのみ、このファイルをコピーしたいのみ」というユースケースはかなりまれでした。コピーを実行するユーザーの使用例。それが必要な場合は、コピーの前にチェックする能力があり、それをスクリプト化することもできます。

そのデザインアプローチを備えたOSがたまたま当時他のすべてのOSを打ち負かしたのはなぜかについて、エッセイの著者はそれについての理論も持っていました。

悪い方が良いという哲学のさらなる利点は、プログラマーがある程度の安全性、利便性、および手間のかかるパフォーマンスを犠牲にしてリソースを適度に使用するように条件付けられることです。ニュージャージーのアプローチを使用して作成されたプログラムは、小規模なマシンと大規模なマシンの両方で正常に機能し、コードはウイルスの上に作成されるため、移植可能になります。

最初のウイルスは基本的に良いものでなければならないことを覚えておくことは重要です。もしそうなら、それが携帯可能である限り、ウイルスの広がりは保証されます。ウイルスが蔓延すると、おそらくその機能を90%近くまで増加させることによって、ウイルスを改善する必要がありますが、ユーザーはすでに正しいものよりも悪いものを受け入れるように条件付けられています。したがって、悪い方が良いソフトウェアは最初に受け入れられ、2番目はユーザーの期待が少なくなるように条件付けられ、3番目はほぼ正しいことまで改善されます。

*-または、作者が「ニュージャージーアプローチ」と呼んだもの

9
T.E.D.

主な理由はGUIは本質的にインタラクティブですですが、/bin/cpのようなバイナリは、あらゆる種類の場所、たとえばGUIから呼び出すことができるプログラムです;-) 。今日でも、/bin/cpへの呼び出しの大部分は、ユーザーがシェルコマンドを入力する実際の端末からではなく、HTTPサーバー、メールシステム、またはNASからのものであると思います。 ユーザーエラーに対する組み込みの保護は、インタラクティブな環境では完全に理にかなっています。単純なバイナリではそれほどではありません。たとえば、GUIは実際の操作を実行するためにバックグラウンドで/bin/cpを呼び出す可能性が高く、ユーザーに尋ねただけの場合でも、標準出力で安全上の質問に対処する必要があります。

必要に応じて、/bin/cpの安全なラッパーを作成するのは簡単なことではありません。 * nixの哲学は、ユーザーにシンプルなビルディングブロックを提供することです。これらのうち、/bin/cpは1つです。