シバン(スクリプト宣言)行が多すぎる-その量を減らす方法はありますか?


12

約20個の小さな.shファイルで構成されるプロジェクトがあります。一般に、20行を超えるコードを持つファイルはないため、これらを「小さい」と命名します。私はモジュール式のアプローチを採用しました。そのため、Unixの哲学に忠実であり、プロジェクトを維持しやすいからです。

.shファイルの最初に、を入れました#!/bin/bash

簡単に言えば、スクリプト宣言には2つの目的があることを理解しています。

  1. これらは、ユーザーがファイルを実行するために必要なシェルを思い出すのに役立ちます(たとえば、ファイルを使用せずに数年後に)。
  2. 別のシェルが使用された場合の予期しない動作を防ぐために、特定のシェル(その場合はBash)でのみスクリプトが実行されるようにします。

プロジェクトが5ファイルから20ファイル、または20ファイルから50ファイルに成長し始めると(この場合ではなく、単に説明のため)、20行または50 のスクリプト宣言があります。一部の人にとっては面白いかもしれませんが、プロジェクトごとに20または50を使用するのではなく、プロジェクトのメインファイルに20または50を使用するのは少し冗長に感じます

いくつかのメインファイルで「グローバル」スクリプト宣言を使用して、この20または50の冗長性、またはスクリプト宣言の非常に多くの行を回避する方法はありますか?


2
すべきではありませんが、できました。削除して、すべてのスクリプトを実行します/bin/bash -x "$script"
-jesse_b


...通常、何かがこの時点に達するまでに、シェルスクリプトの使用を停止し、実際のスクリプト言語を取得して作業を行う時間であると既に結論付けています。
シャドゥール

回答:


19

プロジェクトが50個のBashスクリプトのみで構成されている場合でも、遅かれ早かれPerlやPythonなどの他の言語で記述されたスクリプトの蓄積を開始します(これらのスクリプト言語にはBashにはない利点があります)。

#!各スクリプトに適切な- 行がないと、使用するインタープリターもわからずにさまざまなスクリプトを使用することは非常に困難です。すべてのスクリプトが他のスクリプトから実行されるかどうかは関係ありませんが、これは難易度をエンドユーザーから開発者に移すだけです。これらの2つのグループのどちらも、スクリプトを使用するためにスクリプトが記述された言語を知る必要はありません。

#!-lineなしで明示的なインタープリターなしで実行されるシェルスクリプトは、どのシェルがそれらを呼び出すかによって異なる方法で実行されます(たとえば、どのシェルインタープリターがシバンなしでスクリプトを実行するのか、特にStéphaneの答えを参照してください)実稼働環境(一貫した動作、さらには移植性も必要な場合)。

明示的なインタープリターで実行されたスクリプトは、#!-line の内容に関係なく、そのインタープリターによって実行されます。Pythonや他の言語でBashスクリプトを再実装する場合、これによりさらに問題が発生します。

これらの余分なキーストロークを費やし、常に#!すべてのスクリプトに-行を追加する必要があります。


環境によっては、各プロジェクトの各スクリプトに複数段落の定型文があります。それ#!があなたのプロジェクトで「冗長」であると感じるのが唯一の行であることに非常に満足してください。


答えはこれによって拡張されるべきです:シェル#!は、ファイルに実行可能な許可があり、インタープリターではなくシェルによってロードされるとすぐに、行によってインタープリターを決定します。つまり/path/to/myprog.pl#!行が存在し(perlインタープリターを指している)、ファイルにexec許可がある場合、perlプログラムを実行します。
-rexkogitans

11

あなたはのポイントを誤解してい#!ます。実際には、定義されたインタープリターの下でこのプログラムを実行することは、オペレーティングシステムへの指示です。

そのため、スクリプトが存在#!/usr/bin/perlし、結果のプログラムをasとして実行./myprogramでき、perlインタープリターを使用できます。同様に#!/usr/bin/python、プログラムはpythonで実行されます。

したがって、この行#!/bin/bashは、このプログラムをbashの下で実行するようにOSに指示します。

以下に例を示します。

$ echo $0
/bin/ksh

$ cat x
#!/bin/bash

ps -aux | grep $$

$ ./x
sweh      2148  0.0  0.0   9516  1112 pts/5    S+   07:58   0:00 /bin/bash ./x
sweh      2150  0.0  0.0   9048   668 pts/5    S+   07:58   0:00 grep 2148

したがって、シェルがkshであるにもかかわらず、プログラム "x" #!は行のためにbashの下で実行され、プロセスリストで明示的に確認できます。


ps -aux警告、与えるかもしれない
Weijun周

@WeijunZhou発言...
クサラナンダ

の一部のバージョンでpsは警告が表示されWarning: bad syntax, perhaps a bogus '-'?ます。
周jun順

2
psBSDとUXIXの両方の引数構文をサポートします。 -aux間違ったUNIXスティーブンはおそらく手段でaux正しいBSDある
Jasen

1
人々、2つのこと。(1)明示的な構文ではなく、例の概念(psの呼び出しを示す)に焦点​​を合わせbashます。(2)ps -aux警告なしでCentOS 7およびDebian 9で完全に動作します。そのカットアンドペーストは、まさに私のマシンからの出力でした。-が必要かどうかの議論全体は、現在のバージョンではなく、古いバージョンのlinux procpsに適用されます。
スティーブンハリス

9

オン .sh

悪いのは.sh、ファイル名の最後です。

Pythonでスクリプトの1つを書き換えると想像してください。

さて、#!/usr/bin/python3これで最初の行を変更する必要がありますが、ファイル内の他のすべてのコード行も変更する必要があるため、それほど悪くはありません。ただし、ファイル名をからprog.shに変更する必要もありますprog.py。そして、他のスクリプトのすべての場所、およびすべてのユーザースクリプト(存在することさえ知らなかったスクリプト)を見つけ、それらを変更して新しいスクリプトを使用する必要があります。sed -e 's/.sh/.py/g'あなたが知っているものに役立つかもしれません。しかし、現在、リビジョン管理ツールは多くの無関係なファイルの変更を示しています。

または、Unixの方法で実行し、プログラムにprognot という名前を付けprog.shます。

オン #!

正しいを持っている場合#!、パーミッション/モードを設定して実行を含めます。そうすれば、通訳を知る必要はありません。コンピューターがそれを行います。

chmod +x prog #on gnu adds execute permission to prog.
./prog #runs the program.

GNU以外のシステムについては、マニュアルを読んでchmod(そして8進数を学んでください)、とにかく読んでください。


1
うーん、拡張子は必ずしも悪いわけではありません。少なくとも、他のユーザーとファイルの種類を伝えるのに役立ちます。
セルギーコロディアズニー

@SergiyKolodyazhnyyしかし、あなたは言語を知る必要はなく、ただそれを実行する必要があります。マイクロソフトでさえ、拡張機能から遠ざかろうとしています(残念ながら、私は拡張機能を隠しています)。しかし、私は彼らが良いアイデアになる可能性があることに同意します:.image写真のために。.audioサウンドなどのために、それが何であるかを伝えますが、実装/エンコーディングについては伝えません。
ctrl-alt-delor

1
@ ctrl-alt-delorファイルが呼び出された場合、launch-missilesまたはdelete-project-and-make-manager-pissed言語だけに興味があったときに「実行」したくない場合:)
Sergiy Kolodyazhnyy

2
言語に興味がある場合は、fileコマンドを使用してください。ほとんどのファイルタイプを認識します。
ジェイセン

2
別のシェルスクリプト(つまり、非実行可能スクリプト)をソースとする必要があるスクリプトには.sh拡張子を使用します-そのシナリオでは、拡張子はこれは、ファイルの使用方法を示しているため、重要/有効です。
ローレンスレンショー

8

[hashbang行]は、特定のシェル(その場合はBash)でのみスクリプトが実行されるようにし、別のシェルが使用された場合の予期しない動作を防ぎます。

これはおそらく間違いであると指摘する価値があります。hashbang行は、ファイルを誤ったシェル/インタープリターにフィードしようとすることを妨げるものではありません。

$ cat array.sh
#!/bin/bash
a=(a b c)
echo ${a[1]} $BASH_VERSION
$ dash array.sh
array.sh: 2: array.sh: Syntax error: "(" unexpected

(または同様にawk -f array.shなど)


しかし、いずれにせよ、モジュール性への別のアプローチは、ファイル内にシェル関数を定義し、必要に応じてファイルごとに1つの関数を定義し、次にsourceメインプログラムからファイルを定義することです。そうすれば、ハッシュバンは必要ありません。それらは他のコメントとして扱われ、すべてが同じシェルを使用して実行されます。欠点はsource、関数(例:)を含むファイルが必要になりfor x in functions/*.src ; do source "$x" ; done、それらはすべて同じシェルを使用して実行されるため、そのうちの1つをPerl実装で直接置き換えることができないことです。


たとえば、bash配列の場合は+1。複数のシェルまたはPOSIXで定義されているもののいくつかに不慣れなユーザーは、あるシェルの機能が別のシェルにあると想定する場合あります。関数についても、これは関連する可能性があります: unix.stackexchange.com/q/313256/85039
Sergiy Kolodyazhnyy

4

いくつかのメインファイルで「グローバル」スクリプト宣言を使用して、この20または50の冗長性、またはスクリプト宣言の非常に多くの行を回避する方法はありますか?

あります-移植性またはPOSIX準拠と呼ばれます。常にポータブルでシェルに依存しない方法でスクリプトを記述し#!/bin/sh/bin/sh対話的に使用するスクリプト(または少なくとものようなPOSIX準拠のシェルksh)からのみソースを作成するよう努めてください。

ただし、移植性のあるスクリプトでは次のことからあなたを救うことはできません。

  • シェルのいずれかの機能を使用する必要がある(ilkkachuの答えは一例です)
  • シェル(PythonおよびPerl)よりも高度なスクリプト言語を使用する必要がある
  • PEBKACエラー:スクリプトがないとして、あなたのスクリプトを実行するユーザーJ.Doeの手に渡ってあなたがたとえば、彼らは実行し、それを意図/bin/shして、スクリプトをcsh

私の意見を表明できる場合、排除することによる「冗長性の回避」#!は間違った目標です。目標は、一貫したパフォーマンスであり、実際に動作し、コーナーケースを考慮し、not-parsesing-lsalways-quoting-variables-unless-need-word-splittingなどの特定の一般的な規則に従う実際の作業スクリプトでなければなりません

これらは、ユーザーがファイルを実行するために必要なシェルを思い出すのに役立ちます(たとえば、ファイルを使用せずに数年後に)。

それらは実際にはユーザー向けではありません-オペレーティングシステムが適切なインタープリターを実行するためのものです。この場合の「適切な」とは、OSがシバンにあるものを見つけることを意味します。下のコードが間違ったシェルの場合-それは著者の責任です。ユーザーの記憶に関しては、Microsoft WordやGoogle Chromeなどのプログラムの「ユーザー」は、どの言語プログラムが書かれているかを知る必要はないと思います。著者が必要になる場合があります。

ポータブルスクリプトはPOSIX準拠のシェルで機能するため、移植性のあるスクリプトを作成すると、元々どのシェルを使用したかを覚えておく必要がなくなります。

別のシェルが使用された場合の予期しない動作を防ぐために、特定のシェル(その場合はBash)でのみスクリプトが実行されるようにします。

彼らはしません。実際に予期しない動作を防ぐのは、移植可能なスクリプトを書くことです。


1

#!/bin/bash各スクリプトの先頭に配置する必要があるのは、スクリプトを個別のプロセスとして実行しているためです。

スクリプトを新しいプロセスとして実行すると、OSは(バイナリ実行可能ファイルではなく)テキストファイルであると認識し、実行するインタープリターを知る必要があります。指示がない場合、OSはデフォルト設定(管理者が変更可能)を使用してのみ推測できます。したがって、この#!行は(もちろん実行可能アクセス許可を追加すると)単純なテキストファイルを直接実行できる実行可能ファイルに変換し、OSがそれをどう処理するかを確信します。

#!行を削除する場合のオプションは次のとおりです。

  1. スクリプトを直接実行し(現在実行しているように)、デフォルトのインタープリターがスクリプトと一致することを 望みます-ほとんどのLinuxシステムではおそらく安全ですが、他のシステム(Solarisなど)には移植できません。何でも(vimファイルで実行するように設定することさえできます!)。

  2. を使用してスクリプトを実行しますbash myscript.sh。これは正常に機能しますが、スクリプトへのパスを指定する必要があり、面倒です。

  3. を使用してスクリプトを取得します. myscript.sh。これにより、現在のインタープリタープロセス内で(つまり、サブプロセスとしてではなく)スクリプトが実行されます。しかし、これにはほぼ確実にスクリプトの変更が必要になり、モジュール化/再利用性が低下します。

ケース2と3の場合、ファイルには実行権限は必要ない(必要ない)ため、接尾辞.sh(または.bash)を付けることをお勧めします。

ただし、スクリプトをモジュール化したい(=>独立した自己完結型)と述べたように、これらのオプションはプロジェクトに適していません。したがって#!/bin/bash、スクリプトの先頭に行を置く必要があります。

あなたの質問で、あなたはあなたがUnixの哲学に従っているとも言った。それが本当なら、その#!行が何のためにあるのかを学び、すべての実行可能なスクリプトでそれを使い続ける必要があります!


良い答えをありがとう。私は答えのすべてを愛し、これまでの賛成を考えました:then you should learn what the #! line is really for, and keep using it in every executable script!。U.Philosophyを特定の知識ポイントまで追跡できます。これらすべての答えの後、なぜその用語に含めることができるのか理解しました。答えを編集して、「あなたが尊敬し採用しているUnix哲学の内部部分としてシバンを見る」に置き換えることをお勧めします。
user9303970

1
私が直接だった場合、またはそのコメントが失礼に思えた場合は申し訳ありません。あなたのコメントは、IDEでの作業を希望していることを示唆しています。そのプロジェクト内のスクリプトのグローバルルールを定義できる「プロジェクト」を使用しています。これは有効な開発方法ですが、各スクリプトを独立したスタンドアロンプ​​ログラムとして扱うのには適していません(Unixの哲学を参照)。
ローレンスレンショー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.