LoginSignup
2
4

More than 5 years have passed since last update.

Shell でリダイレクトされない出力の書き方

Last updated at Posted at 2019-02-16

訂正

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 などとすれば、エラー用のファイルを作らずに別端末に直接エラーだけを出力するようにできるというのが面白かった。

参考

2
4
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4