BASHプログラミング
 Computer >> コンピューター >  >> プログラミング >> BASHプログラミング

Bashでプログラミングする方法:論理演算子とシェル拡張

Bashは強力なプログラミング言語であり、コマンドラインやシェルスクリプトで使用するために完全に設計されています。この3部構成のシリーズ(私の3巻のLinux自習コースに基づいています)では、コマンドラインインターフェイス(CLI)でプログラミング言語としてBashを使用する方法について説明します。

最初の記事では、変数や制御演算子の使用など、Bashを使用した簡単なコマンドラインプログラミングについて説明しました。この2番目の記事では、Bashで実行フロー制御ロジックとさまざまなタイプのシェル拡張を提供するファイル、文字列、数値、およびその他の論理演算子のタイプについて説明します。シリーズの3番目で最後の記事では、 for について説明します。 、ながら 、およびまで 反復操作を可能にするループ。

論理演算子は、プログラムで決定を下し、それらの決定に基づいてさまざまな命令セットを実行するための基礎です。これは、フロー制御と呼ばれることもあります。

論理演算子

Bashには、条件式で使用できる論理演算子のセットが多数あります。 ifの最も基本的な形式 制御構造は条件をテストし、条件が真の場合はプログラムステートメントのリストを実行します。演算子には、ファイル演算子、数値演算子、および非数値演算子の3つのタイプがあります。各演算子は、条件が満たされた場合はtrue(0)を返し、条件が満たされない場合はfalse(1)を返します。

これらの比較演算子の機能構文は、角括弧内に配置された演算子を含む1つまたは2つの引数であり、その後に条件がtrueの場合に実行されるプログラムステートメントのリスト、および条件がfalseの場合にオプションのプログラムステートメントのリストが続きます。 :

if [ arg1 operator arg2 ] ; then list
or
if [ arg1 operator arg2 ] ; then list ; else list ; fi

図のように、比較のスペースは必須です。単一の角括弧、 [ および] 、は、テストと同等の従来のBashシンボルです。 コマンド:

if test arg1 operator arg2 ; then list

いくつかの利点を提供し、一部のシステム管理者が好む最近の構文もあります。この形式は、Bashのさまざまなバージョンや、ksh(Kornシェル)などの他のシェルとの互換性が少し低くなります。次のようになります:

if [[ arg1 operator arg2 ]] ; then list
ファイル演算子

ファイル演算子は、Bash内の強力な論理演算子のセットです。図1は、Bashがファイルに対して実行できる20を超えるさまざまな演算子を示しています。スクリプトで頻繁に使用しています。

オペレーター 説明
-ファイル名 ファイルが存在する場合はTrue。空でもコンテンツも含まれている可能性がありますが、存在する限り、これは当てはまります
-bファイル名 ファイルが存在し、 / dev / sdaのようなハードドライブなどのブロック特殊ファイルである場合はTrue または/dev / sda1
-cファイル名 ファイルが存在し、 / dev / TTY1などのTTYデバイスなどの文字特殊ファイルである場合はTrue
-dファイル名 ファイルが存在し、ディレクトリである場合はTrue
-eファイル名 ファイルが存在する場合はTrue。これは-aと同じです 上記
-fファイル名 ファイルが存在し、ディレクトリ、デバイス特殊ファイル、リンクなどではなく、通常のファイルである場合はTrue
-gファイル名 ファイルが存在し、 set-group-idの場合はTrue 、 SETGID
-hファイル名 ファイルが存在し、シンボリックリンクである場合はTrue
-kファイル名 ファイルが存在し、その「スティッキー」ビットが設定されている場合はTrue
-pファイル名 ファイルが存在し、名前付きパイプ(FIFO)である場合はTrue
-rファイル名 ファイルが存在し、読み取り可能である場合、つまり読み取りビットが設定されている場合はTrue
-sファイル名 ファイルが存在し、サイズがゼロより大きい場合はTrue。存在するがサイズがゼロのファイルはfalseを返します
-t fd ファイル記述子がfdの場合はTrue 開いており、端末を参照しています
-uファイル名 ファイルが存在し、その set-user-idが存在する場合はTrue ビットが設定されています
-wファイル名 ファイルが存在し、書き込み可能である場合はTrue
-xファイル名 ファイルが存在し、実行可能である場合はTrue
-Gファイル名 ファイルが存在し、有効なグループIDによって所有されている場合はTrue
-Lファイル名 ファイルが存在し、シンボリックリンクである場合はTrue
-Nファイル名 ファイルが存在し、最後に読み取られてから変更されている場合はTrue
-Oファイル名 ファイルが存在し、有効なユーザーIDによって所有されている場合はTrue
-Sファイル名 ファイルが存在し、ソケットである場合はTrue
file1 -ef file2 file1とfile2が同じデバイスとiNode番号を参照している場合はTrue
file1 -nt file2 file1がfile2よりも新しい場合(変更日による)、またはfile1が存在し、file2が存在しない場合はtrue
file1 -ot file2 file1がfile2より古い場合、またはfile2が存在し、file1が存在しない場合はTrue

図。 1:Bashファイル演算子

例として、ファイルの存在をテストすることから始めます。

[student@studentvm1 testdir]$ File="TestFile1" ; if [ -e $File ] ; then echo "The file $File exists." ; else echo "The file $File does not exist." ; fi
The file TestFile1 does not exist.
[student@studentvm1 testdir]$

次に、 TestFile1という名前のテスト用ファイルを作成します 。今のところ、データを含める必要はありません:

[student@studentvm1 testdir]$ touch TestFile1

$ Fileの値は簡単に変更できます この短いCLIプログラムの複数の場所にあるファイル名のテキスト文字列ではなく変数:

[student@studentvm1 testdir]$ File="TestFile1" ; if [ -e $File ] ; then echo "The file $File exists." ; else echo "The file $File does not exist." ; fi
The file TestFile1 exists.
[student@studentvm1 testdir]$

次に、テストを実行して、ファイルが存在し、長さがゼロ以外であるかどうかを判断します。これは、ファイルにデータが含まれていることを意味します。次の3つの条件をテストする必要があります。1。ファイルが存在しない。 2.ファイルが存在し、空です。 3.ファイルが存在し、データが含まれている。したがって、より複雑な一連のテストが必要です。 elifを使用してください。 if-elif-elseのスタンザ すべての条件をテストするための構成:

[student@studentvm1 testdir]$ File="TestFile1" ; if [ -s $File ] ; then echo "$File exists and contains data." ; fi
[student@studentvm1 testdir]$

この場合、ファイルは存在しますが、データは含まれていません。データを追加して再試行してください:

[student@studentvm1 testdir]$ File="TestFile1" ; echo "This is file $File" > $File ; if [ -s $File ] ; then echo "$File exists and contains data." ; fi
TestFile1 exists and contains data.
[student@studentvm1 testdir]$

それは機能しますが、3つの可能な条件のうちの1つの特定の条件に対してのみ真に正確です。 その他を追加します スタンザを使用すると、より正確になり、ファイルを削除して、この新しいコードを完全にテストできるようになります。

[student@studentvm1 testdir]$ File="TestFile1" ; rm $File ; if [ -s $File ] ; then echo "$File exists and contains data." ; else echo "$File does not exist or is empty." ; fi
TestFile1 does not exist or is empty.

次に、テストする空のファイルを作成します。

[student@studentvm1 testdir]$ File="TestFile1" ; touch $File ; if [ -s $File ] ; then echo "$File exists and contains data." ; else echo "$File does not exist or is empty." ; fi
TestFile1 does not exist or is empty.

ファイルにコンテンツを追加して、もう一度テストします:

[student@studentvm1 testdir]$ File="TestFile1" ; echo "This is file $File" > $File ; if [ -s $File ] ; then echo "$File exists and contains data." ; else echo "$File does not exist or is empty." ; fi
TestFile1 exists and contains data.

次に、 elifを追加します 存在しないファイルと空のファイルを区別するためのスタンザ:

[student@studentvm1 testdir]$ File="TestFile1" ; touch $File ; if [ -s $File ] ; then echo "$File exists and contains data." ; elif [ -e $File ] ; then echo "$File exists and is empty." ; else echo "$File does not exist." ; fi
TestFile1 exists and is empty.
[student@studentvm1 testdir]$ File="TestFile1" ; echo "This is $File" > $File ; if [ -s $File ] ; then echo "$File exists and contains data." ; elif [ -e $File ] ; then echo "$File exists and is empty." ; else echo "$File does not exist." ; fi
TestFile1 exists and contains data.
[student@studentvm1 testdir]$

これで、これら3つの異なる条件をテストできるBash CLIプログラムができました…しかし、可能性は無限大です。

ファイルに保存できるスクリプトのようにプログラムステートメントを配置すると、より複雑な複合コマンドのロジック構造を簡単に確認できます。図2は、これがどのように見えるかを示しています。 if-elif-elseの各スタンザのプログラムステートメントのインデント 構造は論理を明確にするのに役立ちます。

File="TestFile1"
echo "This is $File" > $File
if [ -s $File ]
   then
   echo "$File exists and contains data."
elif [ -e $File ]
   then
   echo "$File exists and is empty."
else
   echo "$File does not exist."
fi

図。 2:コマンドラインプログラムがスクリプトに表示されるように書き直されました

この複雑なロジックは、ほとんどのCLIプログラムには長すぎます。 LinuxまたはBashの組み込みコマンドをCLIプログラムで使用できますが、CLIプログラムが長く複雑になるにつれて、ファイルに保存され、いつでも実行できるスクリプトを作成する方が理にかなっています。将来的に。

文字列比較演算子

文字列比較演算子を使用すると、文字の英数字の文字列を比較できます。これらの演算子は、図3にリストされているもののほんの一部です。

オペレーター 説明
-z文字列 文字列の長さがゼロの場合はTrue
-n文字列 文字列の長さがゼロ以外の場合はTrue
string1 ==string2

または

string1 =string2
文字列が等しい場合はTrue。単一の= POSIX準拠のテストコマンドとともに使用する必要があります。 [[と一緒に使用する場合 コマンドの場合、これは上記のようにパターンマッチングを実行します(複合コマンド)。
string1!=string2 文字列が等しくない場合はTrue
string1 string1がstring2の前に辞書式順序で並べ替える場合はTrue(すべての英数字および特殊文字のロケール固有の並べ替えシーケンスを参照)
string1> string2 string1がstring2の後に辞書式順序でソートされる場合はTrue

図。 3:bash文字列論理演算子

まず、文字列の長さを見てください。 $ MyVarに関する引用符 比較が機能するには、比較に存在する必要があります。 (まだ〜/ testdirで作業している必要があります 。)

[student@studentvm1 testdir]$ MyVar="" ; if [ -z "" ] ; then echo "MyVar is zero length." ; else echo "MyVar contains data" ; fi
MyVar is zero length.
[student@studentvm1 testdir]$ MyVar="Random text" ; if [ -z "" ] ; then echo "MyVar is zero length." ; else echo "MyVar contains data" ; fi
MyVar is zero length.

この方法で行うこともできます:

[student@studentvm1 testdir]$ MyVar="Random text" ; if [ -n "$MyVar" ] ; then echo "MyVar contains data." ; else echo "MyVar is zero length" ; fi
MyVar contains data.
[student@studentvm1 testdir]$ MyVar="" ; if [ -n "$MyVar" ] ; then echo "MyVar contains data." ; else echo "MyVar is zero length" ; fi
MyVar is zero length

文字列の正確な長さを知る必要がある場合があります。これは比較ではありませんが、関連しています。残念ながら、文字列の長さを決定する簡単な方法はありません。それを行うにはいくつかの方法がありますが、 exprを使用すると思います (式の評価)コマンドが最も簡単です。 exprのマニュアルページを読む それが何ができるかについての詳細。テストする文字列または変数の前後に引用符が必要であることに注意してください。

[student@studentvm1 testdir]$ MyVar="" ; expr length "$MyVar"
0
[student@studentvm1 testdir]$ MyVar="How long is this?" ; expr length "$MyVar"
17
[student@studentvm1 testdir]$ expr length "We can also find the length of a literal string as well as a variable."
70
>

比較演算子に関しては、スクリプトで多くのテストを使用して、2つの文字列が等しい(つまり同一である)かどうかを判断します。この比較演算子の非POSIXバージョンを使用しています:

[student@studentvm1 testdir]$ Var1="Hello World" ; Var2="Hello World" ; if [ "$Var1" == "$Var2" ] ; then echo "Var1 matches Var2" ; else echo "Var1 and Var2 do not match." ; fi
Var1 matches Var2
[student@studentvm1 testdir]$ Var1="Hello World" ; Var2="Hello world" ; if [ "$Var1" == "$Var2" ] ; then echo "Var1 matches Var2" ; else echo "Var1 and Var2 do not match." ; fi
Var1 and Var2 do not match.

これらの演算子を試すために、自分でもう少し実験してください。

数値比較演算子

数値演算子は、2つの数値引数を比較します。他の演算子クラスと同様に、ほとんどは理解しやすいです。

オペレーター 説明
arg1 -eq arg2 arg1がarg2と等しい場合はTrue
arg1 -ne arg2 arg1がarg2と等しくない場合はTrue
arg1 -lt arg2 arg1がarg2より小さい場合はTrue
arg1 -le arg2 arg1がarg2以下の場合はTrue
arg1 -gt arg2 arg1がarg2より大きい場合はTrue
arg1 -ge arg2 arg1がarg2以上の場合はTrue

図。 4:数値比較論理演算子をbashする

ここにいくつかの簡単な例があります。最初のインスタンスは変数$Xを設定します 1に設定してから、 $ Xかどうかをテストします 1に等しい。2番目の例では、 X は0に設定されているため、比較は正しくありません。

[student@studentvm1 testdir]$ X=1 ; if [ $X -eq 1 ] ; then echo "X equals 1" ; else echo "X does not equal 1" ; fi
X equals 1
[student@studentvm1 testdir]$ X=0 ; if [ $X -eq 1 ] ; then echo "X equals 1" ; else echo "X does not equal 1" ; fi
X does not equal 1
[student@studentvm1 testdir]$

自分でさらに実験してみてください。

その他の演算子

これらのその他の演算子は、シェルオプションが設定されているか、シェル変数に値があるかを示しますが、変数の値は検出されず、値があるかどうかだけが検出されます。

オペレーター 説明
-o optname シェルオプションoptnameが有効になっている場合はTrue( -o の説明の下にあるオプションのリストを参照してください) Bashのマニュアルページに組み込まれているBashセットのオプション)
-v varname シェル変数varnameが設定されている(値が割り当てられている)場合はTrue
-R varname シェル変数varnameが設定されていて、名前参照である場合はTrue

図。 5:その他のBash論理演算子

自分で実験して、これらの演算子を試してください。

拡張

Bashは、非常に便利なさまざまなタイプの拡張と置換をサポートしています。 Bashのmanページによると、Bashには7つの形式の拡張があります。この記事では、チルダ展開、算術展開、パス名展開、中括弧展開、コマンド置換の5つについて説明します。

ブレースの拡張

ブレース展開は、任意の文字列を生成する方法です。 (このツールは、特殊なパターン文字を使用した実験用に多数のファイルを作成するために以下で使用されます。)中括弧の展開を使用して、任意の文字列のリストを生成し、それらを囲んでいる静的文字列内の特定の場所または静的文字列。これは視覚化するのが難しいかもしれないので、それを行うのが最善です。

まず、ブレース拡張の機能は次のとおりです。

[student@studentvm1 testdir]$ echo {string1,string2,string3}
string1 string2 string3

まあ、それはあまり役に立ちませんね?しかし、少し違った使い方をするとどうなるか見てみましょう。

[student@studentvm1 testdir]$ echo "Hello "{David,Jen,Rikki,Jason}.
Hello David. Hello Jen. Hello Rikki. Hello Jason.

それは何か便利なもののように見えます—それはかなりのタイピングを節約することができます。今これを試してください:

[student@studentvm1 testdir]$ echo b{ed,olt,ar}s
beds bolts bars

先に進むことはできますが、あなたはその考えを理解します。

チルダ拡張

間違いなく、最も一般的な拡張はチルダです( ) 拡張。 cd〜/ Documentsのようなコマンドでこれを使用する場合 、Bashシェルは、ユーザーの完全なホームディレクトリへのショートカットとして展開します。

これらのBashプログラムを使用して、チルダ拡張の効果を観察します。

[student@studentvm1 testdir]$ echo ~
/home/student
[student@studentvm1 testdir]$ echo ~/Documents
/home/student/Documents
[student@studentvm1 testdir]$ Var1=~/Documents ; echo $Var1 ; cd $Var1
/home/student/Documents
[student@studentvm1 Documents]$
パス名の拡張

パス名の拡張は、の文字を使用して、ファイルをグロブするパターンを拡張するための派手な用語です。 および* 、パターンに一致するディレクトリのフルネームに。ファイルグロブとは、さまざまなアクションを実行するときにファイル名、ディレクトリ、およびその他の文字列を柔軟に照合できるようにする特殊なパターン文字を指します。これらの特殊パターン文字を使用すると、文字列内の単一、複数、または特定の文字を照合できます。

  • —文字列内の指定された場所にある任意の文字の1つにのみ一致します
  • * —文字列内の指定された場所にある0個以上の任意の文字に一致します

この拡張は、一致するディレクトリ名に適用されます。これがどのように機能するかを確認するには、 testdir は現在の作業ディレクトリ(PWD)であり、単純なリストから始めます(私のホームディレクトリの内容はあなたのものとは異なります):

[student@studentvm1 testdir]$ ls 
chapter6  cpuHog.dos    dmesg1.txt  Documents  Music       softlink1  testdir6    Videos
chapter7  cpuHog.Linux  dmesg2.txt  Downloads  Pictures    Templates  testdir
testdir  cpuHog.mac    dmesg3.txt  file005    Public      testdir    tmp
cpuHog     Desktop       dmesg.txt   link3      random.txt  testdir1   umask.test
[student@studentvm1 testdir]$

次に、実行で始まるディレクトリを一覧表示します。 、 testdir / Documents 、およびtestdir/ダウンロード

Documents:
Directory01  file07  file15        test02  test10  test20      testfile13  TextFiles
Directory02  file08  file16        test03  test11  testfile01  testfile14
file01       file09  file17        test04  test12  testfile04  testfile15
file02       file10  file18        test05  test13  testfile05  testfile16
file03       file11  file19        test06  test14  testfile09  testfile17
file04       file12  file20        test07  test15  testfile10  testfile18
file05       file13  Student1.txt  test08  test16  testfile11  testfile19
file06       file14  test01        test09  test18  testfile12  testfile20

Downloads:
[student@studentvm1 testdir]$

まあ、それはあなたが望んでいたことをしませんでした。 実行で始まるディレクトリの内容が一覧表示されます 。ディレクトリのみを一覧表示し、その内容は一覧表示しない場合は、 -dを使用します オプション。

[student@studentvm1 testdir]$ ls -d Do*
Documents  Downloads
[student@studentvm1 testdir]$

どちらの場合も、Bashシェルは実行を拡張します *パターンに一致する2つのディレクトリの名前にパターンを入れます。しかし、パターンに一致するファイルもある場合はどうなりますか?

[student@studentvm1 testdir]$ touch Downtown ; ls -d Do*
Documents  Downloads  Downtown
[student@studentvm1 testdir]$

これもファイルを示しています。そのため、パターンに一致するファイルもすべてフルネームに展開されます。

コマンド置換

コマンド置換は、あるコマンドのSTDOUTデータストリームを別のコマンドの引数として使用できるようにする拡張形式です。たとえば、ループで処理されるアイテムのリストとして。 Bashのマニュアルページには、「コマンド置換により、コマンドの出力でコマンド名を置き換えることができます」と書かれています。少し鈍感であれば正確だと思います。

この置換には、`コマンド`の2つの形式があります。 および$(コマンド) 。バックチックを使用した古い形式( ` )、円記号を使用( \ コマンド内の)は、その文字通りの意味を保持します。ただし、新しい括弧形式で使用される場合、円記号は特殊文字としての意味を持ちます。括弧付きの形式では、コマンドステートメントを開いたり閉じたりするために単一の括弧のみが使用されることにも注意してください。

この機能は、あるコマンドの結果を別のコマンドの引数として使用できるコマンドラインプログラムやスクリプトで頻繁に使用します。

この拡張の両方の形式を使用する非常に単純な例から始めます(ここでも、 testdir は障害者です):

[student@studentvm1 testdir]$ echo "Todays date is `date`"
Todays date is Sun Apr  7 14:42:46 EDT 2019
[student@studentvm1 testdir]$ echo "Todays date is $(date)"
Todays date is Sun Apr  7 14:42:59 EDT 2019
[student@studentvm1 testdir]$

-w seqのオプション ユーティリティは、生成された数値に先行ゼロを追加して、値に関係なくすべて同じ幅、つまり同じ桁数になるようにします。これにより、番号順に並べ替えることが簡単になります。

seq ユーティリティは、一連の数字を生成するために使用されます:

[student@studentvm1 testdir]$ seq 5
1
2
3
4
5
[student@studentvm1 testdir]$ echo `seq 5`
1 2 3 4 5
[student@studentvm1 testdir]$

これで、テスト用に空のファイルを多数作成するなど、もう少し便利なことができます。

[student@studentvm1 testdir]$ for I in $(seq -w 5000) ; do touch file-$I ; done

この使用法では、ステートメント seq -w 5000 1から5,000までの数字のリストを生成します。 forの一部としてコマンド置換を使用する ステートメントでは、番号のリストは forによって使用されます ファイル名の数値部分を生成するステートメント。

算術展開

Bashは整数演算を実行できますが、かなり面倒です(すぐにわかります)。算術展開の構文は$((arithmetic-expression))です。 、式を開閉するために二重括弧を使用します。

算術展開は、シェルプログラムまたはスクリプトでのコマンド置換のように機能します。式から計算された値は、シェルによるさらなる評価のために式を置き換えます。

もう一度、簡単なことから始めましょう:

[student@studentvm1 testdir]$ echo $((1+1))
2
[student@studentvm1 testdir]$ Var1=5 ; Var2=7 ; Var3=$((Var1*Var2)) ; echo "Var 3 = $Var3"
Var 3 = 35

次の除算は、結果が1未満の小数値になるため、結果はゼロになります。

[student@studentvm1 testdir]$ Var1=5 ; Var2=7 ; Var3=$((Var1/Var2)) ; echo "Var 3 = $Var3"
Var 3 = 0

これは、スクリプトまたはCLIプログラムでよく行う簡単な計算で、Linuxホストにある仮想メモリの合計量を示しています。 無料 コマンドはそのデータを提供しません:

[student@studentvm1 testdir]$ RAM=`free | grep ^Mem | awk '{print $2}'` ; Swap=`free | grep ^Swap | awk '{print $2}'` ; echo "RAM = $RAM and Swap = $Swap" ; echo "Total Virtual memory is $((RAM+Swap))" ;
RAM = 4037080 and Swap = 6291452
Total Virtual memory is 10328532

`を使用しました コマンド置換に使用されるコードのセクションを区切る文字。

私は主にスクリプト内のシステムリソース量をチェックするためにBash算術展開を使用し、その結果に基づいてプログラム実行パスを選択します。

概要

この記事は、プログラミング言語としてのBashに関するこのシリーズの第2回であり、実行フロー制御ロジックとさまざまなタイプのシェル拡張を提供するBashファイル、文字列、数値、およびその他の論理演算子について説明しました。

このシリーズの3番目の記事では、さまざまなタイプの反復操作を実行するためのループの使用について説明します。


  1. Bashでスペースを含むファイル名の受け渡しを処理する方法

    Linuxには、システムのコマンドを実行するためのデフォルトのシェルBash(別名Bourneagainシェル)があります。ほとんどのプログラマーは、bashが提供する柔軟性と強力なコマンドラインインタープリターのために、cmdよりもbashを好みます。ただし、ほとんどのユーザーは、bashでスペースを含むファイル名の受け渡しを処理しようとすると問題が発生します。これは、bashではスペースがファイル名と同じとは見なされないためです。 スペース付きのファイル名がBashで認識されないのはなぜですか? Bashで、エスケープなしで複数の単語を入力した場合 文字(\)または引用符 、すべて

  2. Scoop を使用して Windows ソフトウェアをインストールおよび更新する方法

    Scoop は、Windows プログラム用の単純なコマンドライン インストーラーです。前のガイドでは、Scoop をインストールし、コマンド ラインで起動して実行する方法を説明しました。この投稿では、Scoop の主な機能の概要を簡単に説明し、Windows でのソフトウェア インストールの管理に Scoop がどのように役立つかを説明します。 通常のインストール手順は次のようなものです。ダウンロード Web サイトに移動し、インストーラーをダウンロードしてプロンプトをクリックします。手順中に気を散らそうとする広告を回避できることを願っています。プログラムを更新するときが来たら、おそらくシ