既存のファイルを静かに上書きするように「cp」が設計されたのはなぜですか?[閉まっている]


30

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ファイルを静かに上書きするのではなく、なぜこのパターンに従わないのですか?


10
coreutilsデザイナーだけが質問に真に答えることができると思いますが、それは今のところ機能しているだけです。通常、アプリは、ユーザーが実際に実行していることを意味し、余分なプロンプトを最小限に抑えることを前提に構築されます。動作を変更する場合は、エイリアス「cp」を「cp -i」または「cp -n」に変更します。
kevlinux

8
@kevlinux coreutils開発者はPOSIX標準を実装しているだけです。
クサラナンダ

17
設計された当時、人々はできることをできるだけ簡潔にしたかったので(コピーではなくcp)、自分が何をしたかを知っていて、ミスをしたときにツールを責めようとしなかったからです。当時のコンピューターはまったく異なる種類の人々でした。心臓外科医のメスがなぜ手を切ることができるのかと尋ねるようなものです。
PlasmaHH

4
Unixは、ユーザーが自分が何をしているかを知っているという前提で、コンピューターの専門家によって設計されました。OSは、ユーザーの手を握ったり、無限の確認を求めたりすることなく、可能であればユーザーが指示したとおりに実行します。操作が何かを上書きした場合、それがユーザーが望んだものであると想定されました。また、これは1970年代初頭(MS DOS以前、Windows、およびホームコンピューター)であり、あらゆる段階でユーザーの手を誘導および保持することはまだ一般的ではなかったことを思い出してください。また、端末としてテレタイプマシンを使用している場合、確認を求めることは常に面倒です。
バールドコッペルド

10
セーフティネットを使用することに慣れてしまい、利用できないシステム(ほとんどの場合)のリスクが大きくなるため、エイリアスcpを作成しないでくださいcp -icp -iそれがあなたが好むものである場合などに定期的に自分自身を教えることをお勧めします。
リード

回答:


52

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

  1. source_fileのタイプが通常ファイルの場合、次の手順が実行されます。

    3.a. dest_fileが存在し、前の手順で書き込まれた場合の動作は指定されていません。それ以外の場合、dest_fileが存在する場合、次の手順が実行されます。

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

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

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

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

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

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

POSIX標準が書かれていたとき、前例はしっかりと確立され、標準の作者は後方互換性を壊さないことの長所をよく知っていました。

その上、他の人が説明したように、ユーザーはシェルエイリアスを使用するか、置換cpコマンドを作成し$PATH、標準システムコマンドの前に置換を見つけるように変更することで、それらの機能を自分で追加/有効にすることができ、その場合はセーフティネットを取得します望ましい。

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

スクリプトの動作が依然としてPOSIX標準に一致する場合、インタラクティブな使用のプロンプトに慣れてから、大量コピーを行うスクリプトを記述します-その後、再び不注意で何かを上書きしていることに気付きます。

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

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

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


54
「なぜこのように機能するのですか?」「規格がそう言っているからです。」「なぜ規格はそう言っているのですか?」「すでに機能しているため、この機能が気に入っています。」
バプティストカンデリエ

16
最後の段落が本当の理由です。確認ダイアログと「これを本当に実行しますか?」プロンプトは弱虫用です:-)
TripeHound

@BaptisteCandellier-同意しました。究極の理由はそこにありますが、食欲をそそるのはこの答えの範囲外です。
TED

2
だからこそ、最後の段落があるrm -rf...あなたが実際にあなたのホームディレクトリにそれを実行するつもりはなかった場合でも、効果的なようです
マックス・ヴァーノン

2
@TEDおかしなことに、誰も誰もunlink(2)syscall「母、私もいいですか?」と尋ねるのにどのように「失敗する」かについて言及していない。:)
tchrist

20

cpUnixの始まりから来ています。Posix標準が書かれる前にそこにありました。確かに、Posix cpはこの点での既存の動作を形式化したばかりです。

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

ですから、当時は、あなたが何をしていたのかを知る必要がありました。なぜなら、コンピュータには、後悔したことを何でもできないようにする力がなかったからです。

(Zevarによる素敵な漫画があります。「zevar cerveaux assiste par ordinateur」を検索して、コンピューターの進化を見つけます。または、http: //perinet.blogspirit.com/archive/2012/02/12/zevar-et-を試してください。cointe.htmlが存在する限り)

本当に興味がある人のために(私はコメントにいくつかの推測を見ました):cp最初のUnix のオリジナルは約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テストや保護手段がなければ困難です。V2 UnixのCコード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-うーん。それが簡単か難しいか、私にはまったくわからないというのはあなたの言うとおりです。コメントを削除しました。
TED

1
@ShadowRangerうん、しかし、それは生産システムになるまでちょうど...道のハードレッスンをプッシュだ
chrylis -on strike-

1
@sourcejedi:楽しい!私の基本的な理論は変わりません(切り捨てで無条件にオープンする方が簡単で、creatたまたまopen+ と同等ですO_CREAT | O_TRUNC)が、存在しないO_EXCLので、既存のファイルをそれほど簡単に処理できなかった理由は説明できません。そうしようとすることは本質的に際どいです(基本的に存在を確認する必要がありますopen/ statその後、使用しますcreatが、大規模な共有システムではcreat、誰かがファイルを作成したときにすでに誰かがファイルを作成していて、とにかくそれを離れて)。同様に無条件に上書きすることもできます。
ShadowRanger

19

これらのコマンドはスクリプトで使用するためのものであり、人間の監督なしで実行される可能性があり、実際にターゲットを上書きしたいケースがたくさんあるためです(Linuxシェルの哲学は、人間が何を知っているかということです彼/彼女がやっている)

まだいくつかの安全対策があります:

  • GNUはcpあり-n| --no-clobberオプション
  • 複数のファイルを単一のファイルにコピーcpすると、最後のファイルがディレクトリではないと文句を言うでしょう。

これはベンダー固有の実装にのみ適用され、ベンダー固有の実装に関する質問ではありません。
18年

10

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

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

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

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

問題は、Unixが40年以上経過していることです。

今すぐ新しいシステムを作成している場合は、さまざまな設計を選択できます。しかし、他の回答で述べたように、Unixを変更すると既存のスクリプトが壊れます。

なぜれた cp静か上書き既存のファイルに設計されましたか?

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

それcpが唯一の問題であることを理解してください。オリジナルのコマンドプログラムはどれもファイルの上書きや削除から保護されていないと思います。シェルには、出力のリダイレクト時に同様の問題があります。

$ 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とディスク容量、遅いプリンターで表示される出力 、システムと開発ソフトウェア。

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

プリンター出力では、行ベースのエディターを使用しますed。これは、視覚的なテキストエディタよりも学習が困難です。現在の行をいくつか印刷し、変更方法を決定して、編集コマンドを入力する必要があります。

使用> second.htmlは、ラインエディタでコマンドを使用するのに少し似ています。その効果は、現在の状態によって異なります。(second.html既に存在する場合、そのコンテンツは破棄されます)。ユーザーは、現在の状態が不明である場合は、彼女を実行するために期待されているlsか、ls second.html最初に。

設計原則としての「単純な実装」

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

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

...

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

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


ターゲットをcp「問題」で上書きするのはなぜですか?対話的に許可を求めたり、失敗させたりすることは、それと同じくらい大きな「問題」かもしれません。
クサラナナンダ

わぁ、ありがとう。ガイドラインを補完します。1)1つのことを実行し、それを適切に実行するプログラムを作成します。2)プログラマーを信頼します。
代数

2
@Kusalanandaのデータ損失は問題です。個人的には、データを失うリスクを減らすことに興味があります。これにはさまざまなアプローチがあります。それが問題だと言っても、代替案にも問題がないわけではありません。
sourcejedi

1
@riderdragon C言語で書かれたプログラムは、Cがプログラマを信頼しているため、非常に驚​​くほど失敗することがよくあります。しかし、プログラマーはそれほど信頼できません。valgrindのような非常に高度なツールを 作成する必要があります。これは、プログラマーが犯す間違いを見つけようとするために必要です。プログラマを信頼せずに「メモリの安全性」を強化しようとするRust、Python、またはC#などのプログラミング言語を持つことが重要だと思います。(C言語は、UNIXをポータブル言語で作成するために、UNIXの作者の1人によって作成されました)。
sourcejedi

1
さらに良いのは、コンテンツのみで上書きcat first.html second.html > first.htmlfirst.htmlれることになりますsecond.html。元のコンテンツは常に失われます。
doneal24

9

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

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

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

  • 正確性-設計は、観察可能なすべての側面において正しいものでなければなりません。正しいよりも単純である方がわずかに優れています。

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

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

強調鉱山

これは1970年だったことを思い出して、「このファイルをコピーしたい」というユースケース がまだ存在しない場合にのみ、コピーを実行する誰かにとってかなりまれなユースケースでした。それがあなたが望むものであるなら、あなたはコピーの前に非常にチェックすることができるでしょう、そしてそれはスクリプト化することさえできます。

その設計アプローチを備えたOSが、当時構築されていた他のすべてのOSに勝った理由については、エッセイの著者もそのための理論を持っていました。

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

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

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


1
これは正しい答えです。
tchrist

+1ですが、具体的な例を挙げると役立つと思います。編集して再コンパイルしたプログラムの新しいバージョンをインストールするとき(およびテストする可能性があります:-)、プログラムの古いバージョンを意図的に上書きする必要があります。(そして、あなたはおそらく、あなたのコンパイラから同様の動作をしたい。だから、初期のUNIXのみ持っているcreat()open()open()それは存在しなかった場合は、ファイルを作成できませんでした。それが唯一の読み取り/書き込み/両方のため0/1/2を取る。それはまだ取っていませんO_CREAT、ありませんO_EXCL)。
sourcejedi

@sourcejedi-申し訳ありませんが、ソフトウェア開発者として、正直なところ、私がコピーを作成するシナリオ以外のシナリオを考えることはできません。:
TED

@TED申し訳ありませんが、私はあなたが間違いなく上書きしたい稀なケースの1つとして、この例を提案しているのです。
sourcejedi

0

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

/bin/cp必要に応じて安全なラッパーを作成することは、初日からささいなことでした。* nixの哲学は、ユーザーにシンプルなビルディングブロックを提供すること/bin/cpです。これらのうち、1つです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.