rcmdnk's blog
Last update

いろいろな貝がら 約100g M70【造形素材・図工・生活 貝・粒状素材】B05-1607

シェルスクリプトにパイプで入力を与えて、その後、キーボード入力を使って 実行することを選択したりしたしする方法について。

標準入力があるかどうか判断して実行するスクリプト

標準入力がある場合はその内容を、そうでない場合は適当に与えられた ファイルの内容を表示するスクリプトを考えます。

a.sh
1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env bash

#if [ ! -p /dev/sdtin ];then
if [ -t 0 ];then
  while read line;do
    echo $line
  done < file
else
  while read line;do
    echo $line
  done
fi

こんな感じ。testコマンドの-tはman testによると

-t file_descriptor
              True if the file whose file descriptor number is
              file_descriptor is open and is associated with a terminal.

と言うことで、上の場合、標準入力0を指定して、これが 現在のターミナルと関連付けられてたりしたらTrueになります。

パイプを使って標準入力を渡したりすると、これが切れるのFalseになります。

同じことが-pを使うと

 -p file       True if file is a named pipe (FIFO).

とのことなので、この場合は/dev/stdinを指定してやって これが名前付きパイプならTrue。 つまり、パイプで標準入力が来るとこの場合はTrueになります。

ので、

$ echo "from file" > $file
$ ./a.sh
from file

とした場合はfileの内容が表示され、 一方

$ echo "from file" > $file
$ echo "from pipe" | ./a.sh
from pipe

の様にパイプ後に使用すると渡された値が表示されます。

上のスクリプトで入力を受け取った後にキー入力をしたい

readでキー入力を受け取って何か操作するようなことを、 上のスクリプトの中で行いたいとします。

単純に何かキーを押したら終了するようなものを入れてみます。

a.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env bash

if [ -t 0 ];then
  while read line;do
    echo $line
  done
else
  while read line;do
    echo $line
  done < file
fi

echo
echo "push any key to exit:"
read -s -n 1 dummy
echo "exit"

これで、そのまま起動すると

$ echo "from file" > $file
$ ./a.sh
from file

push any key to exit:

で止まって、キーを押せばexitを表示されて終了します。 終了後のステータスはもちろん0(正常)です。 (上のreadの無いスクリプトも両方0)

一方、パイプを使って入力を与えると

$ echo "from file" > $file
$ echo "from pipe" | ./a.sh
from pipe

push any key to exit:
$

と、キーを押す前に勝手に終わってしまい、更に終了ステータスを見ると1で正しく終わってません。

最後のreadの所で、入力はパイプからになってるのに、 既に入力は全て上のwhile文で使われてしまってるので そこで失敗しています。

解決法

readの部分に現在の端末からの入力を与えてあげればいいので、 /dev/ttyから入力される様にするだけです。

a.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env bash

if [ -t 0 ];then
  while read line;do
    echo $line
  done
else
  while read line;do
    echo $line
  done < file
fi

echo
echo "push any key to exit:"
read -s -n 1 dummy </dev/tty
echo "exit"

これでパイプで渡しても

$ echo "from pipe" | ./a.sh
from pipe

push any key to exit:

と、待ってくれる様になってキーを押したら正常終了してくれます。

追記

カテゴリーをcoputerにしてしまってURLにcoputerが入ってしまいました。。。 変えるといろいろ面倒だからカテゴリだけ書き換えてURLはそのままにしときます。。。

追記ここまで

Sponsored Links
Sponsored Links

« Zshの配列要素を消す方法 シェルスクリプトで無理やり端末に表示させる方法 »

}