訂正
read -sp "Password: " PASSWORD
の出力先が標準入力になっているものと勘違いしていたが、これは誤りで標準エラーになっていました。
また、コメントで @akinomyoga さんに指摘して頂いた部分も含め大幅に修正した内容となっています。
古い記事は Qiita の編集履歴でご確認ください。
問題
MySQLで該当DBの全`table create statement`を出力する - Qiita の ShellScirpt を書いているときに困ったこと。
read -sp "Password: " PASSWORD
実行後改行が出力されないので、パスワード入力後のエラー発生時などに
$ mysql-all-table-info -p -u master_db > report.txt
Password: mysql command error
と、Password:
の後にエラー文が出てしまう問題。
これを解決するためread -sp
の後にecho
を書いて改行を挟むようにしたのだが、「echo
はリダイレクトされてしまい消えてしまうが、read
はされない」ということに気が付いた。
これはread -sp
の出力先が標準エラー出力になっていたため。echo
も標準エラーにしてあげることでこれが回避できる。
read -sp "Password: " PASSWORD
echo >&2
ただ、そもそも標準出力、標準エラー出力をリダイレクトさせる前提の場合、パスワードの入力を求めても何も表示されなくなってしまう。
かつ、そもそも今回の場合は入力を求める場合、標準入力以外からは求めない。
例えば、mysql
コマンドの場合、標準出力・標準エラー出力・標準入力全てをリダイレクトしてもパスワードの入力が求められる。これと同じことをするにはどうしたらいいのか。
解決
標準出力、標準エラー、標準入力はどれもリダイレクトされる可能性があるので使用できない。
なので、直接端末に出力する方法をとる。
read -sp "Password:" PASSWORD 2> /dev/tty
echo > /dev/tty
-
read
は 標準エラー(ファイル・ディスクリプター番号2
)に出力されるので、それを現在の端末(/dev/tty
)にリダイレクト - echo は標準出力を端末(
/dev/tty
)にリダイレクト
おわりに
今回ググってもそれっぽい情報になかなかたどり着けなかったので書きました。
そもそも、この辺りのことはちゃんと勉強しているエンジニアの方々であれば知っていることだからわざわざ記事にならないのかな…という感じがしました。
今のところの自分の理解では、
- シェルには標準で3つのファイル・ディスクリプタが用意されている
- 0:標準入力(stdin)、1:標準出力(stdout)、2:標準エラー出力(stderr)
- これらはデフォルトでは端末に繋がっている(
/dev/tty
) - パイプ(
|
)やリダイレクト(>
)を使うことで、それらの入出力先を変更できる - パイプやリダイレクトに影響されないようにするには、直接端末のファイル(
/dev/tty
)に出力する
- 出力の流れは
echo => 標準出力(>&1) => /dev/tty => /dev/ttys001 => Terminal
- 今回は標準出力を飛ばすことによって、リダイレクトの影響を受けないようにした
という感じです。
tty
コマンドで、現在の端末のファイル名(/dev/ttys001
など)を調べることができるので、別ウィンドウを開いてtty
を実行し端末ファイル名を調べたら
command 2>> /dev/ttys002
などとすれば、エラー用のファイルを作らずに別端末に直接エラーだけを出力するようにできるというのが面白かった。
参考