markdown

はじめに (対象読者・この記事でわかること)

本記事は、Linux のシェル上で「コマンドの標準出力をファイルにリダイレクトしたい」ものの、実行途中で対話的に入力を要求されてしまうケースに悩んでいるエンジニアやサーバー管理者を対象としています。
具体的には、sshmysqlgit commit など、実行時にパスワードや確認メッセージを標準入力から受け取るコマンドを、スクリプトやバッチ処理の中で自動化したい方が対象です。

この記事を読むと、以下のことができるようになります。

  • 対話的コマンドでも標準出力だけをファイルに保存できる手法を理解する
  • expect、ヒアドキュメント、パイプ入力など複数の実装パターンを選択できる
  • 実践的なサンプルスクリプトを手元に持ち、トラブルシューティングのポイントを把握できる

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。

  • 基本的な Unix/Linux のシェル操作(bash、zsh 等)
  • 標準入力(stdin)・標準出力(stdout)・標準エラー出力(stderr)の概念
  • grepawk などのテキスト処理コマンドの基本的な使い方

リダイレクトの基本と対話的コマンドの問題点

Unix 系 OS では、>>> で標準出力をファイルへリダイレクトできますが、コマンドが実行中に 標準入力からの入力を要求 すると、リダイレクトだけでは処理が止まってしまいます。たとえば以下のようなケースです。

Bash
$ mysql -u root -p -e "SELECT NOW();" > result.txt Enter password: ← ここで対話的に入力待ちになる

標準入力がブロックされると、リダイレクト先のファイルは生成されても中身が空になるか、途中で処理が中断されます。この状況を回避するためには、標準入力に対して適切にデータを供給しながら、標準出力はそのままファイルへ流す必要があります。代表的な対策としては次の 4 つがあります。

  1. Here Document(ヒアドキュメント)
    スクリプト内部で入力データを埋め込む方法。<<EOF で開始し、EOF で終了します。

  2. expect スクリプト
    対話的プロンプトを自動的に検知し、あらかじめ用意した文字列で応答させるツール。複雑な対話フローでも柔軟に対応可能です。

  3. パイプと printf / yes
    シンプルに文字列や改行だけを流すだけで済むケースは、printfyes を組み合わせると手軽です。

  4. stdbuf / unbuffer と組み合わせた非同期入力
    バッファリングの影響でプロンプトが見えにくい場合に、出力をフラッシュさせつつ入力を自動化します。

それぞれの手法は、コマンドの性質やセキュリティ要件(パスワードを平文で保存したくない等)に応じて選択します。以降では、実際に例を挙げながら 「MySQL のパスワード入力」「Git のコミットメッセージ入力」 の二つのシナリオで、上記手法を適用する具体的手順を示します。

対話的コマンドを非対話的に実行する具体的手順

ステップ 1:Here Document(ヒアドキュメント)で入力を埋め込む

シンプルな入力が固定文字列だけの場合は、ヒアドキュメントが最も手軽です。以下は MySQL にパスワードを渡す例です。

Bash
#!/bin/bash # password.txt などに平文で保存したくない場合は、環境変数や安全な保管場所で取得してください。 MYSQL_PWD="MySecretPw" mysql -u root -p"${MYSQL_PWD}" -e "SELECT NOW();" <<EOF > result.txt EOF

ポイントは -p"${MYSQL_PWD}" のようにコマンドラインでパスワードを渡すことです。MySQL は -p オプションに続く文字列を直接パスワードとして解釈するため、対話的プロンプトは発生しません。ヒアドキュメント自体は空でも構いません。

注意点:コマンドラインにパスワードが露出するため、ps コマンドで他プロセスから閲覧されるリスクがあります。実運用では ~/.my.cnf[client] セクションで password=... を書くか、環境変数 MYSQL_PWD を利用する方が安全です。

ステップ 2:expect スクリプトで対話的プロンプトを自動応答

複雑な対話フロー(複数回の確認や、動的に表示されるエラーメッセージ)には expect が有効です。以下は ssh 接続時にパスフレーズを自動入力し、リモートで ls -l の結果をローカルファイルに保存する例です。

Tcl
#!/usr/bin/expect -f set timeout -1 set host "example.com" set user "deploy" set password "MySSHPass" set outfile "ssh_output.txt" spawn ssh ${user}@${host} "ls -l /var/www" expect { "yes/no" { send "yes\r"; exp_continue } "password:" { send "${password}\r" } } log_file -a $outfile expect eof

使い方

Bash
chmod +x ssh_expect.tcl ./ssh_expect.tcl

expect は対話的テキストに対してパターンマッチングを行い、マッチしたらあらかじめ指定した文字列を送信します。log_file コマンドで標準出力をファイルに保存でき、spawn の直後に log_file を置くと、内部の標準エラーも同時に取得できます。

ステップ 3:printf とパイプで短い入力を流す

入力が単一行・固定文字列で済むケースは、printf を使うとコードが最もシンプルです。たとえば Git のコミットメッセージを対話的に入力させる代わりに、以下のようにします。

Bash
printf "Fixed bug in login flow\n" | git commit -F - > commit_log.txt

-F - オプションは「標準入力からコミットメッセージを読む」ことを指示します。これにより、エディタが起動せずに直接コミットが完了し、標準出力は commit_log.txt に保存されます。

ステップ 4:yes コマンドで多数の yes/no 確認を自動承諾

インストーラや apt-get install のように「Continue? [Y/n]」と繰り返し表示される対話がある場合は、yes が便利です。

Bash
yes | sudo apt-get install -y some-package > install.log

yes は標準入力に y と改行を無限に流すため、すべての確認を自動で「Yes」にできます。ただし、危険度が高い コマンドに対しては必ず -y オプション等公式に自動承諾を指示できるフラグがあるか確認してください。

ステップ 5:stdbufunbuffer で出力バッファリングを制御しつつ入力を自動化

対話的プログラムはしばしば標準出力がブロックされ、プロンプトが見えないことがあります。そのような場合は stdbuf(GNU coreutils)や unbuffer(expect パッケージ)でバッファリングを無効化し、リアルタイムに出力を取得しながら入力を自動化します。

Bash
stdbuf -o0 -e0 my_interactive_cmd <<EOF > output.txt input_line_1 input_line_2 EOF

上記は -o0 -e0 によって標準出力・標準エラーのバッファを 0 バイトに設定し、即時にファイルへ書き込む例です。

ハマった点やエラー解決

発生した問題 原因 解決策
expect スクリプトでパスワードが送信されない spawn 後に expect がプロンプト文字列を正しく認識できていない 正規表現で改行やカラースキームを除外し、expect -re "password: " に変更
printf パイプで git commit が失敗した git がデフォルトでエディタを起動し、-F - が無視された git commit --no-edit -m "msg" または git commit -F - と明示的に指定
yes が膨大なログを生成した yes が無制限に y を出力し、> log.txt が巨大に head -n 1000 等で出力行数を制限、もしくは yes の代わりに printf "y\n" を単回使用
stdbuf がコマンドに対応していない 一部のバイナリは stdbuf のラッパーを通すと失敗する unbuffer を代替として使用、または script -q -c "cmd" で擬似端末を作成

解決策のまとめ

  • 入力が固定文字列のみprintf / Here Document
  • 複数ステップの対話expect(柔軟かつスクリプト化しやすい)
  • 多数の yes/no 確認yes(ただし安全性を要確認)
  • バッファリングでプロンプトが見えないstdbuf / unbuffer
  • パスワードやシークレットは環境変数・設定ファイルで管理 → コマンドライン露出回避

まとめ

本記事では、対話的に入力を要求されるコマンドでも、標準出力を安全にファイルへリダイレクトする手法 を体系的に解説しました。

  • Here Document とコマンドラインオプション で最もシンプルに対処
  • expect スクリプト が複雑な対話フローでも自動化できる
  • printf / yes が単純入力・多数確認に最適
  • stdbuf / unbuffer でバッファリング問題を回避

これらの技術を組み合わせることで、バッチ処理や CI/CD パイプラインでの自動化が格段に楽になります。次回は、SSH エージェントや Vault を活用したシークレット管理と、上記手法の組み合わせ例を紹介する予定です。

参考資料