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

# チーム内発表会用。

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

---

# about me

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

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

---

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

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

---

# 今回は

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

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

---

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

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

---

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

```bash:to_ruby.sh
NAME='James Brown'

ruby from_shell.rb
```

---

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

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

---

# やっていることは

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

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

---

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

```bash
$ 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.sh` で `NAME` の中身を確かめるようにしてみます。

```bash: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` は環境変数ではなくなってしまったんですね。。。

---

ここから分かったことは

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

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

=> [サーバエンジニアの知恵袋
](http://seesaawiki.jp/w/engineernochiebukuro/d/%A5%B7%A5%A7%A5%EB%CA%D1%BF%F4%A4%C8%B4%C4%B6%AD%CA%D1%BF%F4)
>通常、慣例として環境変数は大文字アルファベットから変数名を使用し、シェル変数は小文字アルファベットからなる文字列を使用する。しかし、機能的にはどちらの変数にどちらを使用しても構わない。


---

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

```bash:readonly.sh
readonly NAME
NAME='James Brown'

echo $NAME
```

シェル内で NAME を変更しようとするとエラーになります。
変数は元のまま ( Michel Jackson ) のままであることが分かります。
 
```bash
$ NAME='Michel Jackson' bash readonly.sh
readonly.sh: line 2: NAME: readonly variable
Michel Jackson
```

---

これを元に [あなたが扱っているのは本当に環境変数ですか？](http://qiita.com/YumaInaura/items/69c6935aa5193630da07) という記事をQiitaに書いたところ、 6 Stock をもらったので、ある程度は需要がある情報だったようです。

![image](https://qiita-image-store.s3.amazonaws.com/0/89618/36074286-208f-c1d9-0f19-565b81206c02.png)

---


# 結論

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

---

# ところで

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

という話をしましたが、

Google が [Shell Style Guide](https://google.github.io/styleguide/shell.xml) というのを作っているようです。

(あくまで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.

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

```bash
# 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」の変数としている例。

```bash
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.

```bash
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.

```bash
# 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.

```bash
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.

---

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

#  (終)








<!-- Update From Qiita API -->

# チャットメンバー募集


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

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





# Twitter


https://twitter.com/YumaInaura


<!-- Update From Qiita API -->