💭

HOGE=xxx && コマンドの挙動を調べる

に公開

遭遇したこと

以下のコマンドを実行するシェルを実行したら、uvicorn内で動くアプリケーションにHOGEという変数が渡らないということがあった。

HOGE=xxx && \
uvicorn main:app --port 8080

&& は 左のコマンドの終了ステータス(echo $?)が0のときに右のコマンドが実行されるやつ。

なので、HOGE=xxxとuvicornはそれぞれ別コマンド扱いだから、反映されていなかったのだろうか?と思い、

HOGE=xxx \
uvicorn main:app --port 8080

こうすればうまく動いた。めでたしめでたし。

ふと気になったこと

ここからはある意味蛇足。

HOGE=xxxという感じの変数とそれを実行するコマンドを && でつなげたら基本は、変数使えないよね?
と思って調べてたときに、以下のコマンドだとちゃんと変数の中身が見れた。

$ HOGE=xxx && echo $HOGE
xxx

HOGE=xxxecho $HOGEは別プロセスになるだろうから、空文字が出力されるのを期待していた。

そもそも別プロセスなんだろうか?というのを見てみたときに、それも一緒だった。

$$でいまのプロセス番号を調べる。

$  echo $$ && echo $$
15253
15253

psしてみると、いま起動しているターミナルと同じプロセスだった。

$  ps | grep zsh
  15253 pts/0    00:00:00 zsh

echoが組み込みコマンドなので、同じプロセス内で起動するということな気がする

$ which echo
echo: shell built-in command

なので実質、HOGE=xxx とターミナルで打ったあとに、echo $HOGEと打つのと同じ。

$ HOGE=xxx
$ echo $HOGE
xxx

ちょっと実験

以下のようなスクリプトecho_test.shを用意する

echo $$

それをコマンドラインで実行する

$ echo $$ && ./echo_test.sh
15253
99279

実質 echo $$ && echo $$ と変わらないわけなのだが、./echo_test.shは別プロセスとして起動している。

以下のようなスクリプトecho_test.shを用意する

echo $$

次にecho_test.shを修正する

echo $HOGE

ターミナルで実行すると、空文字が出力される。
ちなみに、上記を実行したあとに、echo $HOGEってターミナルで打つと、xxxが出力される。HOGE=xxxが反映されているのは、このターミナルで起動しているプロセスのみだということがわかる。

$  HOGE=xxx && ./echo_test.sh
# 何も出ない

# 続けざまにecho $HOGEすると、ちゃんと設定されている
$ echo $HOGE
xxx

冒頭でやったような、&&をつけずに、ひとつのコマンドとして実行すれば、ちゃんと設定したとおりに出力される。
なお当たり前ではあるのだが、同じターミナルで echo $HOGE すると、今度は何も出ない。

$ HOGE=xxx  ./echo_test.sh 
xxx

$ echo $HOGE
# 何も出ない

あとは、.bashrc.zshrcでよくやる export を設定すれば && つきでいける。
exportは子プロセスにもその定義した変数を渡すことができるので。

$ export HOGE=xxx && ./echo_test.sh
xxx

$ echo $HOGE
xxx

exportのちょっと面白かった動き

実験中、面白い挙動するなと思ったのが、exportで定義した変数を書き換えるやつ。

echo_test.shを呼び出す別のスクリプトでvariable_test.shを用意する。

HOGE=aiueo && ./echo_test.sh

# echo_test.sh
# echo $HOGE

ターミナル上でHOGEを何もexportしていなければ、出力されない。
HOGE=aiueoとしているのも、variable_test.shのプロセス内のみであり、後続のecho_test.shに引き継がれないからだ。

$ ./variable_test.sh 
# 何も表示されない

ただ、HOGEをexportすると変わる。

$ export HOGE=xxx && ./variable_test.sh 
aiueo

てっきりexportしたHOGE=xxxが適用されて、xxxと出力されると思ったけど、variable_test.sh内でaiueoに書き換えられて、それがそのまま次のecho_test.shのプロセスへ引き継がれるらしい。

exportコマンドで、exportして子プロセスに引き継がれる変数を列挙できるので、それを使って確認をしてみる。

variable_test.shを以下に変更

# HOGE=aiueoしたあとに、HOGEがどう変わっているのか見てから、./echo_test.shを呼び出す
HOGE=aiueo && export | grep HOGE ; ./echo_test.sh

ターミナル上で実行してみる。

$  export HOGE=xxx && export | grep HOGE ;  ./variable_test.sh
HOGE=xxx
HOGE=aiueo
aiueo

1行目がvariable_test.shを実行する前のexportしたHOGEの中身。
2行目がvariable_test.sh内でaiueoに変更したあとの、exportしたHOGEの中身。

一度exportされた変数は、ずっと子プロセスに引き継がれるし、途中で書き換えてしまうこともできるんだなとわかった。

おわりに

そもそも変数定義のあとに && をつける意味ってほぼ無い気はしている(そもそも変数定義で失敗する状況が思いつかない)。

Discussion