日々之迷歩

世の中わからんことだらけ

ITが複雑で難しくなっていく様に翻弄される日々です。微力ながら共著させていただいた「シェル・ワンライナー160本ノック」をよろしくお願い申し上げまする。

対話モードか否か?

10月末に行われた第19回シェル芸勉強会の問題をふと思い出すことがあった。

最初の問題が、端末で実行した場合とシェルスクリプトに書いて実行した場合で動きを変える、という妙ちくりんな興味深い問題だった。解答例ではaliasが効くかどうかで判定していた。

問題と解答は、下記のQ1を参照。

【問題と解答】第19回シェル芸3周年記念勉強会 | 上田ブログ

この問題の意味は、シェルの状態が対話(インタラクティブ)モードかどうか?の判定をどうするかということだと思う。

ところでFreeBSDのデフォルトログインシェルはtcshFreeBSDのサーバで作業をしていたところ。ふとシェルの設定ファイルを覗いてみた。デフォルトで提供される設定ファイルが下記のようになっている。

> tail -n +24 .cshrc

if ($?prompt) then
  # An interactive shell -- set some stuff up
  #set prompt = "%N@%m:%~ %# "
  #set promptchars = "%#"
  set prompt = '\n%n@%M:%~\n%# '

  set filec
  set history = 1000
  set savehist = (1000 merge)
  set autolist = ambiguous
  # Use history to aid expansion
  set autoexpand
  set autorehash
  set mail = (/var/mail/$USER)
  if ( $?tcsh ) then
    bindkey "^W" backward-delete-word
    bindkey -k up history-search-backward
    bindkey -k down history-search-forward
  endif
endif

if文のところを見ると、プロンプトが設定されたシェル変数promptが定義されているかどうかで、シェルの状態が対話(インタラクティブ)モードなのか?どうか判定している。

つまり下記のような状態になるということ。シェルはtcshの場合。スクリプト(非対話モード)で実行するやり方として、tcsh -c 'コマンド文字列'というやり方もあるので、こちらでやってみる。

## 対話モード
> echo $prompt
\n%n@%M:%~\n%#

## 非対話モード
> tcsh -c 'echo $prompt'
prompt: Undefined variable.

ところで、sh、bashzshのプロンプトはPS1というシェル変数にプロンプトの設定がされている。tcshと同様に、対話モードかどうかで変化するのか確認してみた。
シェルはbashの場合。

## 対話モード
$ echo $PS1
\n\u@\h:\w\n\$

## 非対話モード
$ bash -c 'echo $PS1'

これだとPS1が未定義なのかどうかがよくわからないので、set -uを使って確認してみる。

## 対話モード
$ set -u; echo $PS1; set +u
\n\u@\h:\w\n\$

## 非対話モード
$ bash -c 'set -u; echo $PS1'
bash: PS1: 未割り当ての変数です

つまり、非対話モードの場合はシェル変数PS1が未定義らしい。

ということで、シェル変数PS1の文字列が空じゃないかどうかで判定してみた。解答はこちら。

$ [ -n "$PS1" ] && echo 1ppm || echo 40ppm
1ppm

$ bash -c '[ -n "$PS1" ] && echo 1ppm || echo 40ppm'
40ppm