速い調査の終わりに、Bashはチューリングは完全言語です。。
なぜBashが比較的簡単なスクリプトを書くのにほとんど排他的に使われるのか疑問に思います。 LinuxにはBashシェルが付属しているため、他の広く使用されているコンピュータ言語に必要なように、外部ソルバーやコンパイラなしでシェルスクリプトを実行できます。これは、場合によっては言語自体の平凡さを補うことができる大きな利点です。
それでは、そのようなプログラムがどれほど複雑になる可能性があるかという制限はありますか?複雑なプログラムを書くために純粋なBashを使うことはできますか?純粋なBashでファイルコンプレッサー/圧縮解除器を作成できますか?翻訳者?シンプルなビデオゲーム?
デバッグツールがあまりにも制限されていて、あまり使われていないからでしょうか?
ベストアンサー1
BashはTuringの完全言語のようです。
コンセプトチューリングの完全性言語に役立つ他の多くの概念から完全に分離大規模プログラミング:使いやすさ、表現力、理解性、スピードなど
チューリングの完全性だけが必要な場合は、プログラミング言語はありません。別の言葉、さらにアセンブリ語。コンピュータプログラマが直接書く。機械語コード、私たちのCPUもTuring Completeだからです。
比較的単純なスクリプトを書くためにBashがほとんど排他的に使用されるのはなぜですか?
configure
GNU Autoconfの出力などの大きくて複雑なシェルスクリプトは、次のようないくつかの理由で一般的ではありません。
最近まで、どこでもPOSIX互換シェルを期待することはできません。。
多くのシステム、特に古いシステムには、技術的にPOSIX互換シェルがあります。どこかにたとえば、
/bin/sh
シェルスクリプトを作成していて、複数の異なるシステムで実行する必要がある場合はどうすればよいですか?シェルボーンライン? 1つのオプションは引き続き使用することですが/bin/sh
、そのようなシステムで実行されている場合は、POSIX以前のBourneシェル方言に制限することを選択してください。POSIX以前のBourneシェルには、呼び出す必要がある算術機能も組み込まれていませんでした。
expr
またはbc
これを完了してください。POSIXシェルを使用しても見逃すことがあります連想配列Perlが初めて人気を博して以来、Unixスクリプト言語で私たちが期待した他の機能1990年代初頭。
この歴史的事実は、現代のBourneシェルスクリプトインタプリタファミリの多くの強力な機能が純粋にどこでも利用できるとは期待できないため、何十年も無視してきた伝統があることを意味します。
実際、このような状況は今日まで続きます。 Bashは連想配列を取得しません。バージョン4までしかし、Bash 3に基づいて、まだ使用しているシステムがどれだけ多いかを知ると驚くことがあります。 Appleは2017年にもまだmacOSでBash 3を提供しています -明らかにライセンス上の理由で- そして、Unix / Linuxサーバーはほとんどまたはまったく影響を受けずに長い間本番環境で実行されることが多いため、CentOS 5システムなどのBash 3を実行し続ける安定した古いシステムがある可能性があります。ご使用の環境にこれらのシステムがある場合、そのシステムで実行する必要があるシェルスクリプトで連想配列を使用することはできません。
この質問に対する答えが「最新の」システム用のシェルスクリプトのみを書くことであれば、ほとんどのUnixシェルへの最後の共通参照は次のようになることに直面する必要があります。POSIX シェル標準1989年の導入以来、本質的に変わりませんでした。この標準に基づくさまざまなシェルがありますが、すべてさまざまな程度から逸脱します。再連想配列を使用すると、および
bash
すべてzsh
このksh93
機能を持ちますが、いくつかの実装非互換性があります。もしそうなら、あなたの選択はただBashを使用するかただZshを使用するかただ使用ksh93
。その質問に対する答えが「ちょうどBash 4をインストールしてください」などであれば、
ksh93
Perl、Python、またはRubyを「ただ」インストールするとどうでしょうか?これはほとんどの場合許可されていません。デフォルト値が重要です。Bourneシリーズのシェルスクリプト言語はサポートされていません。基準寸法。
シェルスクリプトでモジュールシステムに最も近いのは、コマンド
.
(より近代的なBourneシェルバリアントとも呼ばれますsource
)です。このコマンドは、適切なモジュールシステムに関していくつかのレベルで失敗します。その中で最も基本的なものは次のとおりです。名前空間。どのプログラミング言語を使用しても、より大きなプログラム全体の単一のファイルが数千行を超えると、人間の理解に問題が発生し始めます。私たちが大きなプログラムを多くのファイルで構成する本当の理由は、その内容を最大1〜2文で抽象化できるからです。ファイルAはコマンドラインパーサ、ファイルBはネットワークI / Oポンプ、ファイルCはライブラリZとプログラムの残りの間のシムです。複数のファイルを単一のプログラムに組み合わせる唯一の方法がテキストを含む場合は、プログラムが合理的に大きくなる可能性があるサイズの制限を設定します。
比較のために、Cプログラミング言語リンカーはなく、
#include
ステートメントのみがあります。これらのC-lite方言には、extern
または同じキーワードは必要ありませんstatic
。これらの機能はモジュール性のために存在します。POSIXメソッドが定義されていません。ファイルだけでなく、単一のシェルスクリプト関数までの変数の範囲を指定します。
これは効果的ですすべての変数はグローバルこれは再びモジュール性と構成性を損なう。
bash
もちろん、POSIX以降のシェルにはこの問題に対する解決策がありますが、ksh93
これはzsh
上記の1番目の項目に戻ります。GNU Autoconfマクロ作成スタイルガイドでこの内容を確認できます。彼らは推薦するマクロ自体の名前を変数名の前に付けると、変数名が非常に長くなります。純粋に衝突の可能性をゼロに近いレベルに減らすためです。
この点では、CでさえCより1マイル優れています。ほとんどのCプログラムは主に関数ローカル変数を使用して作成されるだけでなく、Cはブロックスコープもサポートしているため、単一関数内の複数のブロックが交差汚染なしで変数名を再利用できます。
Shellプログラミング言語には標準ライブラリはありません。
シェルスクリプト言語の標準ライブラリは、以下の内容と
PATH
言えます。より強力言語が始まります。Perlのように広く使用されているシェルユーティリティライブラリのアーカイブもありません。CPAN。利用可能なサードパーティユーティリティの大規模なコードベースがない場合、プログラマはより多くのコードを手動で記述する必要があるため、生産性が低下します。
ほとんどのシェルスクリプトが便利なタスクを実行するために通常Cで書かれた外部プログラムに依存しているという事実を無視しても、これはすべてオーバーヘッドを生成します。
pipe()
→fork()
→exec()
コールチェーン。このモードは、以下と比較してUnixでかなりうまく機能します。産業用コンピュータ他のオペレーティングシステムでプロセスを開始しますが、ここではサブルーチンの呼び出しより効率的な他のスクリプト言語を使用してください。これはシェルスクリプトの実行速度の上限を厳しく制限する。シェルスクリプトには、並列実行によってパフォーマンスを向上させるいくつかの組み込み機能があります。
Bourneシェルは
&
この目的のためにパイプとパイプを提供しますが、これは通常CPUまたはI / O並列処理を達成するためのものではなく、複数のプログラムを作成するためにのみ適しています。wait
できないようですつながるコアを使用するか、シェルスクリプトを使用してRAIDアレイを飽和させる場合は、他の言語を使用してより良いパフォーマンスを得ることができます。特に、パイプラインは並列実行を介してパフォーマンスを向上させる弱い方法です。 2つのプログラムのみを並列に実行でき、2つのプログラムのうちの1つは禁止特定の時点でお互いへの入出力です。
この問題を解決するための最近の方法は次のとおりです。
xargs -P
そして牛に似た一種の栄養parallel
しかし、これは上記の4番目の項目に移動するだけです。シェルスクリプトは、マルチプロセッサシステムに組み込まれている機能を実際に最大限に活用できないため、システム内のすべてのプロセッサを使用できる言語で書かれたプログラムよりも常に遅くなります。 GNU Autoconf
configure
スクリプトを再び例に挙げると、システムのコア数を2倍に増やすことは、システムの実行速度を上げるのにほとんど役に立ちません。-
これにより、他のプログラミング言語で簡単に実行できる多くのタスクを実行できなくなります。
一方、プログラムメモリ内の他のデータ構造を間接的に参照できないことは、組み込みデータ構造に限定されることを意味します。データ構造。あなたのシェルに連想配列しかし、どのように実装されますか?いくつかの可能性があり、それぞれ異なる長所と短所があります。レッドブラックツリー、AVLツリーとハッシュテーブル最も一般的ですが、他のものもあります。異なるトレードオフが必要な場合は、参照なしに複数の種類の高レベルのデータ構造を手動でローリングできないため、ブロックされます。あなたは与えられたものに閉じ込められています。
あるいは、データ構造が必要ですが、シェルスクリプトインタプリタに組み込まれている適切な選択肢でさえないかもしれません。方向性非循環グラフ、モデリングに必要な場合があります。依存関係グラフ。私は何十年もの間プログラミングをしてきましたが、シェルスクリプトでこれを行うために考えることができる唯一の方法は乱用です。ファイルシステム、無効な参照としてシンボリックリンクを使用します。これは、ソリューションがエレガントな、迅速であるか、理解しやすいのか不明なチューリングの完全性にのみ依存する場合に得られるものです。
高レベルのデータ構造は、ポインタと参照の1つの用途にすぎません。持つ他の多くのアプリこれは、Bourneシリーズのシェルスクリプト言語では簡単にはできません。
続けることができますが、ポイントを理解しているようです。簡単に言えば多い。より強力Unix型システム用のプログラミング言語。
これは、場合によっては言語自体の平凡さを補うことができる大きな利点です。
もちろん、これがGNU Autoconfがconfigure
スクリプト出力に意図的に制限されたBourneシェルスクリプト言語スイートのサブセットを使用する理由です。したがって、configure
スクリプトはほぼどこでも実行できます。
移植性に優れたBourneシェル方言で書くことが実用的であると信じているGNU Autoconf開発者よりも多くの人を見つけることはできません。しかし、彼ら自身の作品はほとんどPerlで書かれています。m4
、わずかなシェルスクリプトだけがAutoconfのものです。出力純粋なBourneシェルスクリプトです。これが「Bourne Identity」という概念がどれほど有用であるかについて疑問を提起しない場合、私はどうなるかわかりません。
それでは、そのようなプログラムがどれほど複雑になる可能性があるかという制限はありますか?
チューリングの完全性観察からわかるように、技術的にはそうではありません。
しかし、これは大規模なシェルスクリプトが作成しやすく、デバッグしやすく、実行速度が速いという意味ではありません。
純粋なbashでファイルコンプレッサー/圧縮解除器を作成できますか?
PATH
「純粋な」 Bash、か。コンプレッサーは16進エスケープシーケンスを使用できますが、echo
そうするのはかなり困難です。圧縮解除器は、次の理由でこのように書き込むことはできません。シェルはバイナリデータを処理できません。。あなたは結局電話します。od
などを使用してバイナリデータをテキスト形式に変換します。これはシェルの基本的なデータ処理方法です。
意図したとおり、シェルスクリプトをドライバの他のプログラムの接着剤として使用する方法について話し始めると、他のPATH
プログラミング言語が実行できる操作が制限されているため、制限がまったくないため、ドアが開きます。他のプログラムを呼び出すことで完全な機能を得るシェルスクリプトは、PATH
より強力な言語で書かれた単一のプログラムほど速くは実行されません。するランニング。
それがポイントです。迅速に実行されるプログラムが必要な場合、または他の人の機能を借りるのではなく、それ自体で強力でなければならない場合は、そのプログラムをシェルに書き込むことはありません。
シンプルなビデオゲーム?
これは皮をむいたテトリス。検索してみると他の同様のゲームもあります。
非常に限られたデバッグツールのみ利用可能
大規模なプログラミングをサポートするために必要な機能のリストでは、デバッグツールのサポートランクは20位です。多くのプログラマーがもっと依存しています。printf()
デバッグ言語に関係なく、適切なデバッガよりも優れています。
シェルには、があり、echo
一緒にset -x
使用すると多くの問題をデバッグするのに十分です。