🖥

チーム内発表会用 | これからシェルの(環境変数の)(わりと基本の)話をする

2023/08/26に公開

チーム内発表会用。

Qiitaのスライドが便利なので、使います。


about me

シェルけっこう初心者です。
だいたいスクリプトは perl で書いてきました。

「シェル難しそう」「魔術めいてるし」と思ってました。
でも最近は、シェルをよく触っています。


力量的には、チームメンバーと比較するとこんな感じかなーと思ってます。

某Yさん
某Yさん
某Yさん
⇡
⇡
⇡
----------(雲)------------
⇡
⇡
⇡
⇡
⇡
⇡
⇡
Y (me)
⇡
----------(地上)----------
⇡
環境変数がよく分からない
⇡
for in って何?
⇡
echo って何?

今回は

環境変数についての誤解していたことをお話します。

シェルを知ってる人にとっては基本の話だと思うので、
途中で正解を発表者に教えてたくなるかもしれませんがゆっくりご覧ください。


それはある日のことでした。

ここに二つのごく簡単なスクリプトがあります。


受け取った環境変数 ( NAME ) を強制的に書き換え、rubyに渡すシェルスクリプト。

to_ruby.sh
NAME='James Brown'

ruby from_shell.rb

シェルから環境変数 ( NAME ) を受け取って、標準出力するスクリプト。

from_shell.rb
puts "#{ENV['NAME']} in ruby"

やっていることは

「シェルで環境変数を書き換えて、それをrubyで表示する」ということです。

(この例では素の ruby になってますが、問題に行き当たったのは rake 先生と遊んでいた時でした )


シェルスクリプトを実行すると、 環境変数 NAME に何を渡しても、James Brown が表示されます。

$ NAME='Michel Jackson' bash to_ruby.sh 
James Brown in ruby

まったく意味がないスクリプトなので、実行時にも環境変数を省略しましょう。
これで、単に James Brown が表示されるスクリプトになるはずです。

$ bash to_ruby.sh 

ところが結果は。。。
James Bronn が消えてしまいました。

$ bash to_ruby.sh 
 in ruby

そこで to_ruby.shNAME の中身を確かめるようにしてみます。

to_ruby.sh
NAME='James Brown'

echo $NAME in shell

ruby from_shell.rb

そしてまた同じコマンドを実行すると。。。

$ bash to_ruby.sh 
James Brown in shell
 in ruby

シェルには James Brown がいるのに、rubyからは消えてしまっていることが分かりました。


つまりシェルの変数 NAME は環境変数ではなくなってしまったんですね。。。


ここから分かったことは

  • 環境変数 = 大文字
  • シェル変数 = 小文字

というイメージがあったのですが、それはあくまでも「慣習」にすぎないということです。

=> サーバエンジニアの知恵袋

通常、慣例として環境変数は大文字アルファベットから変数名を使用し、シェル変数は小文字アルファベットからなる文字列を使用する。しかし、機能的にはどちらの変数にどちらを使用しても構わない。


環境変数を書き換え不能にするには、 readonly をつけるのが良いですね。

readonly.sh
readonly NAME
NAME='James Brown'

echo $NAME

シェル内で NAME を変更しようとするとエラーになります。
変数は元のまま ( Michel Jackson ) のままであることが分かります。

$ NAME='Michel Jackson' bash readonly.sh
readonly.sh: line 2: NAME: readonly variable
Michel Jackson

これを元に あなたが扱っているのは本当に環境変数ですか? という記事をQiitaに書いたところ、 6 Stock をもらったので、ある程度は需要がある情報だったようです。

image


結論

  • スクリプト内で、いきなり大文字の変数代入 ( NAME=James Brown ) をしても、環境変数にはならない。
  • スクリプト内で環境変数を作る場合は export ( export NAME=James Brown ) と書く。
  • いちど環境変数に「なった」変数は、普通の変数と同じように代入できる。 ( NAME=James Brown )
  • スクリプト起動時に NAME='James Brown' to_ruby.sh のように書いた場合、シェルスクリプト内で export したのと同じ効果が得られる。

ところで

環境変数 = 大文字
変数 = 小文字

という話をしましたが、

Google が Shell Style Guide というのを作っているようです。

(あくまでGoogle内のルールっぽい?)


環境変数や定数は大文字で、ファイル先頭に書こうぜ。

Constants and Environment Variable Names

All caps, separated with underscores, declared at the top of the file.
Constants and anything exported to the environment should be capitalized.

「定数、なおかつ環境変数」を宣言する方法も載っていた。

# Constant
readonly PATH_TO_FILES='/some/path'

# Both constant and environment
declare -xr ORACLE_SID='PROD'

ソースファイルはスネークケースで書こうぜ。ハイフン禁止。(ここでは拡張子がないように見える)

Source Filenames

Lowercase, with underscores to separate words if desired.
This is for consistency with other code styles in Google: maketemplate or make_template but not make-template.


readonly として使う変数は明示的に readonly しようぜ。

Read-only Variables

Use readonly or declare -r to ensure they're read only.
As globals are widely used in shell, it's important to catch errors when working with them. When you declare a variable that is meant to be read-only, make this explicit.

zipのバージョンを「readonly」の変数としている例。

zip_version="$(dpkg --status zip | grep Version: | cut -d ' ' -f 2)"
if [[ -z "${zip_version}" ]]; then
  error_message
else
  readonly zip_version
fi

関数の中だけで使う変数は local 宣言しようぜ。

Use Local Variables

Declare function-specific variables with local. Declaration and assignment should be on different lines.

my_func2() {
  local name="$1"
..

バッククォートのかわりに $() を使おうぜ。
ネストするのも読むのも簡単だから。

Use $(command) instead of backticks.

Nested backticks require escaping the inner ones with . The $(command) format doesn't change when nested and is easier to read.


パイプでの連結が1行に収まらない場合は、複数行に分けようぜ。

Pipelines

Pipelines should be split one per line if they don't all fit on one line.
If a pipeline all fits on one line, it should be on one line.
Pipelines should be split one per line if they don't all fit on one line.

# All fits on one line
command1 | command2

# Long commands
command1 \
  | command2 \
  | command3 \
  | command4

ループ内で使う変数は、展開元の変数と似たものにしようぜ。

Variable Names
As for function names.
Variables names for loops should be similarly named for any variable you're looping through.

for zone in ${zones}; do
  something_with "${zone}"
done

そもそもどんな時にシェルを使うのか。
シンプルな仕事のため、他のプログラムのラッパーとして使おうぜ。

When to use Shell

Shell should only be used for small utilities or simple wrapper scripts.


どのシェルを使えば良いのか。
bash が唯一の選択肢 だぜ。

Which Shell to Use

Bash is the only shell scripting language permitted for executables.


シェルって可愛いやつですね!

(終)

チャットメンバー募集

何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ

Twitter

https://twitter.com/YumaInaura

公開日時

2016-08-04

Discussion