10月末に行われた第19回シェル芸勉強会の問題をふと思い出すことがあった。
最初の問題が、端末で実行した場合とシェルスクリプトに書いて実行した場合で動きを変える、という妙ちくりんな興味深い問題だった。解答例ではaliasが効くかどうかで判定していた。
問題と解答は、下記のQ1を参照。
【問題と解答】第19回シェル芸3周年記念勉強会 | 上田ブログ
この問題の意味は、シェルの状態が対話(インタラクティブ)モードかどうか?の判定をどうするかということだと思う。
ところでFreeBSDのデフォルトログインシェルはtcsh。FreeBSDのサーバで作業をしていたところ。ふとシェルの設定ファイルを覗いてみた。デフォルトで提供される設定ファイルが下記のようになっている。
> 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、bash、zshのプロンプトは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