忍者ブログ

プログラミングの練習

プログラミングの問題やプログラミング関連知識、ソフトウェアのテストについてのブログです

【Bash】2つのファイルを同時に1行ずつ並行処理する方法|exec とファイル記述子(3< , 4<)の活用

Bashで複数ファイルの行を突き合わせたり並行して処理したりしたい場合、ファイル記述子(ファイルディスクリプタ)を複数割り当てる手法が非常に有効です。

今回は exec 3<file1 と exec 4<file2 を使い、2つのテキストファイルを同時に1行ずつ読み込んで処理する具体的なスクリプトと注意点を解説します。





1. サンプルコードと実行結果

2つの入力ファイルからそれぞれの行を同時に読み込み、セットで整形出力するスクリプトです。


  • ① 準備するテキストファイル

    # file1.txt(サーバー名リスト)

    server-A

    server-B

    server-C



    # file2.txt(IPアドレスリスト)

    192.168.1.10

    192.168.1.20

    192.168.1.30

  • ② ソースコード(parallel_read.sh)

    #!/bin/bash



    # ファイル記述子 3 と 4 にファイルを割り当て

    exec 3< file1.txt

    exec 4< file2.txt



    # どちらか一方の読み込みが成功する限りループ継続

    while true; do

      IFS= read -u3 -r line1

      res1=$?

      IFS= read -u4 -r line2

      res2=$?



      # 両方のファイルが終端(EOF)に達したらループを抜ける

      if [ $res1 -ne 0 ] && [ $res2 -ne 0 ]; then

        break

      fi



      echo "Host: ${line1:-N/A} | IP: ${line2:-N/A}"

    done



    # ファイル記述子を明示的にクローズ

    exec 3<&-

    exec 4<&-

  • ③ 実行結果

    $ ./parallel_read.sh

    Host: server-A | IP: 192.168.1.10

    Host: server-B | IP: 192.168.1.20

    Host: server-C | IP: 192.168.1.30



2. スクリプトの仕組みと重要ポイント

複数ファイルを同時に扱う際の技術的ポイントを解説します。


  • Point:ファイル記述子の個別管理と終了判定

    ・exec 3< file1 / exec 4< file2:番号「3」と「4」のファイル記述子を独立してオープンします。

    ・read -u3 / read -u4:それぞれの記述子から1行ずつ個別に読み取ります。

    ・終了ステータス($?)の判定:read コマンドはファイルの終端(EOF)に達すると 0 以外の返り値を出力します。両方の変数が非0になった段階でループを抜ける構成にしています。

    ・後処理(exec 3<&-):使い終わった記述子を明示的に解放することで、リソースリークを防ぐ安全な実装になります。

【解説と補足ポイント:行数が異なる場合の対策(デフォルト値設定)】



■ 行数が揃っていない場合の安全策

2つのファイルの行数が異なる場合(片方が4行で片方が3行など)、片方がEOFに達すると空文字("")が読み込まれます。



スクリプト内ではパラメータ展開の構文 ${line1:-N/A} を使うことで、行が不足している場合に任意の値(この例では N/A)を自動挿入できます。これにより、行数のズレによるエラーや表示崩れを防止可能です。



3. まとめ

Bashで複数テキストファイルを同時に並行読み込みする場合は、exec 3< ... と read -u の組み合わせが最も確実で柔軟な方法です。


ログや設定値の突き合わせ処理、2つのリストを連携させた一括自動化スクリプトなどで非常に重宝するテクニックです。


PR

【Bash応用】ファイル記述子(ファイルディスクリプタ)を活用したファイル読み込み|exec と read -u の仕組み

Bashスクリプトでファイルを読み込む際、done < sample.txt のようにループの末尾でリダイレクトする以外に、ファイル記述子(ファイルディスクリプタ)を直接開いて管理する方法があります。

今回は exec コマンドと read -u オプションを使った高度なファイル読み込みの仕組みとメリットについて解説します。





1. サンプルコードと実行結果

ファイル記述子番号「3」を割り当ててファイルをオープンし、ループ内で読み出すスクリプトです。


  • ① ソースコード(read_fd.sh)

    #!/bin/bash

    exec 3<sample.txt

    while read -u3 line

    do

      echo $line

    done

  • ② 実行結果

    $ ./read_fd.sh

    abc

    123

    456



2. exec と read -u の仕組み解説

通常の while read line ... done < file との違いや、各コマンドの挙動を整理します。


  • Point:exec によるファイル記述子の永続的割り当て

    ・exec 3<sample.txt:現在のシェルプロセス上で ファイル記述子番号「3」 を作成し、sample.txt を読み取り専用として開きます。スクリプトが終了するか、明示的に閉じるまでファイルが開きっぱなしの状態で維持されます。

    ・read -u3 line:標準入力(通常はキーボード・番号0)ではなく、ファイル記述子番号 3 から1行読み込むことを直接指定するオプションです。

    ・通常のリダイレクト(done < file)のように「ループの範囲」に縛られず、スクリプト内の任意の場所で順番にファイルから読み込むことができます。

【解説と補足ポイント:この書き方が活躍する実践的ユースケース】



■ 1. 複数のファイルを同時に1行ずつ読み込むとき

通常のリダイレクトでは1つの入力しか扱いにくいですが、ファイル記述子を分けることで(例:3<file1.txt、4<file2.txt)、2つのファイルを並行して1行ずつ比較・処理できます。



■ 2. ループ内で対話型コマンドや SSH 接続を行うとき

while read line; do ssh host ...; done < list.txt のようにループ内で ssh コマンドなどを実行すると、ssh側が残りのテキスト行(標準入力)を全て横取りしてしまい、ループが1回で終わるトラブルが発生します。

read -u3 でファイル記述子を標準入力(0)から切り離しておくことで、この干渉(標準入力のバッティング)を完全に防止できます。



■ 明示的にクローズする作法

使い終わったファイル記述子は、以下の記述で閉じる(クローズする)のがベストプラクティスです。

exec 3<&- # ファイル記述子 3 を閉じる



3. まとめ

exec 3<ファイル名 と read -u3 は、Bashにおいてファイル記述子を明示的に操作するための手法です。


複数ファイルの並行処理や、ループ内での外部コマンド(SSHやscp等)実行時の標準入力バッティング回避など、高度なスクリプト作成に欠かせない重要なテクニックです。



【Bash入門】ファイルを1行ずつ読み込む基本パターン|while read line とリダイレクトの使い方

Bashスクリプトでテキストファイルやログファイルを一行ずつ読み込んで処理したい場合、while read ループ とリダイレクト(<)を組み合わせるのが最も一般的かつ効果的な手法です。

今回はその基本構造と実行例について解説します。





1. サンプルコードと実行例

対象ファイルの内容を1行ずつ変数へ格納し、ループ内で出力するスクリプトです。


  • ① ソースコード(read_file.sh)

    #!/bin/bash

    while read line

    do

      echo $line

    done < sample.txt

  • ② 読み込み対象ファイル(sample.txt)

    abc

    123

    456

  • ③ 実行結果

    $ ./read_file.sh

    abc

    123

    456



2. while read line の仕組みとポイント

処理の流れと構文の重要なポイントをまとめます。


  • Point:入力リダイレクトによる効率的な読み込み

    ・while read line:標準入力から1行取得して変数 line にセットし、ファイルの終端(EOF)に達するまでループ処理を継続します。

    ・末尾のリダイレクト < sample.txt:done の後ろに < ファイル名 を指定することで、ループ体全体の標準入力をファイルに変更します。

    ・ファイルを一括でメモリに読み込まず1行ずつ処理するため、大容量のログファイルでもメモリを圧迫せずに安全に処理できるのが強みです。

【解説と補足ポイント:実務で必須のハマりポイント対策(IFS= IFS= read -r)】



■ 実務でよく使われる安全な定型文 IFS= read -r line

基本文法(read line)の場合、行頭・行末の半角スペースやタブが自動で削られたり、バックスラッシュ(\)が特殊文字として解釈される挙動が発生します。

原文を保持して読み込みたい実務では、以下のように書くのが標準的です。

while IFS= read -r line

do

  echo "$line"

done < sample.txt
・IFS=:フィールド区切り文字を空に設定し、前後の空白削除を防止。

・-r:バックスラッシュのエスケープ処理を無効化し、そのままの文字列として取得。



3. まとめ

Bashでファイルを1行ずつ処理する基本は、while read line ... done < ファイル名 の構成です。


空白やバックスラッシュを含むデータを扱う際は、IFS= read -r line を使用するテクニックもあわせて押さえておきましょう。



【Bash入門】read -p で対話型スクリプトを作成する|標準入力とプロンプト表示の基本

Bashスクリプトでユーザーからのキーボード入力を受け取りたい場合、シェル組み込みコマンドの read を使用します。

今回は、オプション -p を使ってプロンプトメッセージを表示しながら変数へ代入する、最も基本的かつ便利な書き方を解説します。





1. サンプルコードと実行例

キーボードから入力された値を変数に格納し、そのまま画面へ出力するシンプルなスクリプトです。


  • ① ソースコード(sample.sh)

    #!/bin/bash

    read -p "Enter number: " var1

    echo $var1

  • ② 実行結果

    $ ./sample.sh

    Enter number: 2

    2



2. read -p のポイント解説

なぜ read -p を使うのか、その仕組みとメリットを整理します。


  • Point:プロンプト表示と入力を1行で記述できる

    ・-p オプション:入力を促すプロンプト文字列(例:"Enter number: ")を画面に表示します。

    ・変数への格納:入力された値が直後の変数名(例:var1)へ自動的に代入されます。

    ・通常であれば echo -n "Enter number: " と read var1 の2行に分けて書く処理を、1行にすっきり集約できるのが最大のメリットです。

【解説と補足ポイント:実務で使える組み合わせて覚えたいオプション】



■ パスワード入力用の非表示(-s オプション)

パスワードやAPIキーなどの機密情報を入力させる場合、入力文字を画面に表示させない -s(silent)オプションと組み合わせます。

read -sp "Enter Password: " password


■ タイムアウトの設定(-t オプション)

指定した秒数以内に利用者が入力しなかった場合、処理を自動で進めたり終了させたりする設定が可能です。

read -t 10 -p "10秒以内に入力してください: " var1



3. まとめ

対話型シェルスクリプトを作成する際は、read -p "メッセージ" 変数名 の構文を覚えておくと非常にスマートに記述できます。


必要に応じて非表示化(-s)や制限時間(-t)などのオプションと組み合わせ、ユーザーフレンドリーなスクリプトを作成しましょう。



【Java進化史 第1回】Java7のtry-with-resourcesは何を変えたのか?close地獄とコネクションリークを防ぐ仕組み

Java7は「地味なバージョンアップ」と言われることもありますが、開発現場の事故を劇的に減らした非常に重要な機能が追加されました。

それが「try-with-resources構文」です。今回は、かつての「close地獄」の背景から、この機能がもたらした本質的な価値について解説します。





1. 昔の「close地獄」とメモリ・接続リークの恐怖

Java6までの時代、ファイルやデータベース接続などのリソースを扱う処理は冗長で、エラーが発生しやすい構造になっていました。


  • ① Java6までの冗長な記述例

    リソースの破棄処理を確実に行うため、finally ブロック内でさらに null チェックと try-catch を書く必要がありました。

    FileInputStream fis = null;

    try {

        fis = new FileInputStream("test.txt");

    } catch (IOException e) {

        e.printStackTrace();

    } finally {

        if (fis != null) {

            try {

                fis.close();

            } catch (IOException e) {

                e.printStackTrace();

            }

        }

    }

  • ② 開発者を悩ませた3つの問題点

    ・記述のネスト地獄:本質的なロジックよりも解放処理のコード量が圧倒的に多くなる。

    ・close忘れ:記述箇所が増えることで、うっかり解放処理を漏らしてしまう。

    ・DB接続リーク:解放漏れが発生するとDBコネクションが枯渇し、ある日突然システム全体が停止する重大障害につながる。



2. コネクションプール利用下でもcloseが必要な理由

HikariCPなどの「JDBCコネクションプール」を導入している現場でも、close() の呼び出しは極めて重要です。


  • コネクションプールにおける close() の役割

    ・プール環境での close() は物理的な切断ではなく、プールへの「返却」を意味します。

    ・close() を呼び出さないと接続がプールに戻らず、空き接続の枯渇 $\rightarrow$ 処理待ちの発生 $\rightarrow$ 最悪システム停止 という深刻なトラブルを引き起こします。



3. Java7「try-with-resources」でどう変わったか

Java7で導入された try-with-resources 構文を使うと、コードの記述量が劇的に減少し、自動安全処理が行われます。


  • ① 新しい記述パターン

    try の後の丸括弧 () 内で対象のリソースを宣言・生成します。

    try (FileInputStream fis = new FileInputStream("test.txt")) {

        // 処理内容

    } catch (IOException e) {

        e.printStackTrace();

    }
    ・自動クローズ:try ブロックを抜けた時点で、明示的に書かなくても自動的に close() が呼ばれます。

    ・コードの簡略化:finally ブロックもネスト構造も不要になります。

  • ② 仕組みの核となる「AutoCloseable」インターフェース

    この機能が利用できるのは、java.lang.AutoCloseable インターフェースを実装しているクラスに限られます。

    標準ライブラリの多くが対応しており、自作クラスでも実装可能です。

    // 自作リソースで AutoCloseable を実装する例

    class MyResource implements AutoCloseable {

        @Override

        public void close() {

            System.out.println("closed");

        }

    }

【解説と補足ポイント:実務における運用のコツ】



■ 独自バッチや検証用コードでのミスに注意

大規模開発ではDBアクセス処理などが共通フレームワーク化されていることが多いため、日常業務で手動クローズを書く機会は少ないかもしれません。

しかし、「検証用の使い捨てコード」や「簡単なバッチ処理」を個人で作成する際、従来通りの解放漏れ事故が発生しやすいため注意が必要です。



■ 旧方式(Java6スタイル)との混在リスク

既存のレガシーコードと新構文が混在するとコードレビューの負担が増え、レビュー漏れのミスを誘発します。既存資産の全面改修は困難でも、新規作成・機能改修時には必ず try-with-resources を標準ルールとして採用しましょう。



4. まとめ

try-with-resources は、単に「コードを短く書くための糖衣構文(シンタックスシュガー)」にとどまらず、リソース管理の統一ルールを作り、重大事故を未然に防ぐための強力な設計思想です。


Javaのバージョンアップによる進化には派手な新機能だけでなく、このように現場のトラブルを根本から解決する堅牢な改善が詰まっています。