1. 序章

Pipelines は、Bashの非常に強力で用途の広い機能です。 これらは、Bashまたはオペレーティングシステムによって提供されるネイティブコマンドで最も一般的かつ簡単に使用されます。 パイプラインまたはパイプは、常に最も効率的な方法であるとは限らず、 find コマンドのように、特定のシェルコマンドが出力の処理をネイティブにサポートしている場合は冗長になることもあります。 :

find -exec some_script {} \;

ただし、コマンドが標準出力のネイティブ処理をサポートしておらず、データをシェルスクリプトにストリーミングする必要がある場合は、出力をBash関数にパイプ処理するのが適切なオプションです。

このチュートリアルでは、最初にスクリプトにパイプアウトし、次にそのスクリプト内の関数にパイプアウトすることで基礎を築き、最後に安全に行う方法について説明します。

2. スクリプトにパイプする方法

いくつかの基本をもう一度見てみましょう。 Bashのパイプは、あるプロセスの標準出力を受け取り、それを標準入力として別のプロセスに渡します。 Bashスクリプトは、コマンドラインで渡すことができる位置引数をサポートしています。

これらの引数は、スクリプト内でbashで定義された変数 $0から$9を使用して取得できます。

$ more myscript.sh
#!/bin/bash 
echo $1

ここで、パラメーターを使用してスクリプトを呼び出すと、変数 $ 1 にキャプチャされ、シェルに出力された最初の引数を確認できます。

$ ./myscript.sh hello
hello

指導原則#1:Bashで実行されるコマンドは、それらを開始するプロセスから標準入力を受け取ります

これは、いくつかの未定義のデータをファイルにcatする基本的なスクリプトsample_one.shを定義することで確認できます。 cat はファイルまたはマウントポイントである引数を取る必要があるため、現時点では未定義のデータですが、何も定義されていません。

$ more sample_one.sh
#!/bin/bash 
cat > file_one.txt

コマンド(この場合は date、)を実行し、出力をスクリプトにパイプすると、結果をfile_one.txtで表示できます。

$ date | ./sample_one.sh 
$ more file_one.txt 
  Sat Oct 23 22:03:33 SAST 2021

これは少し不思議に見えるかもしれませんが、2つのことの組み合わせです。

  • sample_one.sh は、それを開始したプロセスから標準入力を受け取ります( date | )。
  • cat の設計により、スタンドアロンまたは引数なしで使用される場合、標準入力をコピーして標準出力にリダイレクトします。

学んだことを利用して、それを独自のカスタムスクリプト内の関数に適用できるようになりました。

3. 関数にパイプする方法

指導原則#2: スクリプトに渡される標準入力には、最初に呼び出された関数からアクセスできます。

今回は関数内で行うことを除いて、以前のコードを再利用することでこれを検証できます。 スクリプトは他の方法では実行されないため、関数( read_stdin )を呼び出すことを忘れないでください。

$ more sample_two.sh
#!/bin/bash
function read_stdin()
{
  cat > file_two.txt
}
read_stdin

関数は、標準入力を受け入れるか保持するために宣言された引数または変数を必要としないことに注意することが重要です。 これはコマンドラインから自動的に渡され、親プロセスから関数に継承されます。 date コマンドを再度使用して、 file_two.txt の内容を確認できます。これは、以前と同じです。もちろん、時刻が更新され、オペレーティングシステムの定義されたロケール

$ date | ./sample_two.sh
$ more file_two.txt
Sat Oct 23 23:03:33 SAST 2021

すばらしい作業です。bash関数への出力のパイプを完成させました。 しかし、これはあまり読みやすくなく、経験の浅い人にとっては維持するのが難しく、バグの可能性があります。 改善しましょう。

4. 安全に配管する方法

4.1. / dev /stdinによる読みやすさ

読みやすさを追求し、Unixの設計(すべてがファイル)に従って、ファイル / dev /stdinを使用して標準入力を参照できます。

$ more sample_three.sh
#!/bin/bash
function read_stdin()
{
  cat < /dev/stdin > file_three.txt
}
read_stdin

file_three.txt の出力は、以前に見たものと同じです。 この方法の利点は、標準入力が cat の隠された知識に包まれていないが、目に見えて変数に割り当てられていることです。 これにより、新しい開発者または管理者がデバッグとトラブルシューティングにかかる時間を節約できます。

4.2. 読み取りによる保守性

パイプから関数への入力を受け取るためのもう1つのメカニズムは、readユーティリティです。 これにより、1つの論理行が標準入力から1つ以上のシェル変数に読み込まれます。

$ more sample_four.sh
#!/bin/bash
function getInput()
{
  read in echo $in > file_four.txt
}
getInput

標準入力をユーザー定義のシェル変数$inに読み込みます。 また、 cat を使用する代わりに、 echo を使用して、その変数の内容を標準出力とfile_four.txtに書き込みます。

4.3. テストによる堅牢性

これまでのサンプルスクリプトには、よくある問題が1つあります。それは、脆弱です。 ユーザーが実際に標準入力を渡さずにそれらのいずれかを実行した場合、スクリプトはハングします。 実際、入力を無期限に待機します。 以下の例のように、これをシミュレートできます。

$ ./sample_one.sh

これを修正するには、最初に test を使用して入力を検証し、変数が設定されているかどうかを確認します。 実際、位置引数にはtestを使用することもできます。

たとえば、ファイルの種類を確認して値を比較するだけのテストを作成できます。

$ more sample_five.sh
#!/bin/bash
function getInput() {
    if test -n "$1"; then
        echo "Read from positional argument $1";
    elif test ! -t 0; then
        echo "Read from stdin if file descriptor /dev/stdin is open"
        cat > file4.txt
    else
        echo "No standard input."
    fi
}
getInput

このスクリプトを詳しく見てみましょう。

  • test-nパラメーターとともに最初に条件付きで使用すると、位置引数の長さがゼロより大きいかどうかがチェックされます。
  • test の2番目の条件付き使用は、 -t パラメーターを使用して、標準の入力ファイル記述子が端末で開いているかどうかをチェックします。
  • else ブロックは、標準入力がなかったことを確認します。

これは間違いなく、標準入力を検証するためのより堅牢なソリューションであり、簡単に使用できます。 ほとんどのユースケースをカバーしていますが、端末にバインドされるという欠点があります。 より高度な入力検証については、 moreutils 、特にifneバイナリを検討できます。

5. 結論

このチュートリアルでは、最初にスクリプトを介して、次に関数を介して、標準出力を関数にパイプする方法を段階的に学習しました。 その後、コードの読みやすさ、保守性、堅牢性を向上させることで、標準入力を安全に配管することに注目しました。