【初心者向け】Linuxでのfor文の使い方（変数とか）

## はじめに
この記事では私自身、for文を使おうとしてよくわからなかった部分（とくに変数の使い方あたり）について書いていこうと思います。

## そもそも for文の使い方

```sh:for文の基本的な形
for 変数 in 値のリスト
do
処理
done
```
Linuxでfor文を使うには、以下のような方法があります。
#### 1. シェルスクリプトとして読み込む
適当なエディタ（メモ帳とか）でファイルを作り（拡張子は **.sh**にする）、上の形でfor文を書いて、Linux上で読み込みます。
ファイルの先頭行に`#!/bin/bash`という文字を書いておきます。

```sh:メモ帳などで作る（hoge.sh）
#!/bin/bash
for i in 1 2 3
do
echo $i        ## 1 2 3 を画面に出力する
done
```
作ったファイル（シェルスクリプト）を読み込むには、Linuxの画面上で**ファイル名を打ち込めばOK**です。ただしファイルのパスを指定しなければいけないので「**./ファイル名**」（ **.** は**現在のディレクトリ**を指す）とします。（※作ったファイルは事前に現在のディレクトリ内に配置しておきます）

```sh:出力結果
$ ./hoge.sh
1
2
3
```

#### 2. コマンドライン上で書く（複数行）
上のようにメモ帳に書いた内容をLinuxの画面上にコピペしてもOKです。
その場合、行替えごとに`>`マークが自動でつきます。

```sh:コマンド＆出力結果
$ for i in 1 2 3
> do
> echo $i
> done
1
2
3
```

#### 3. コマンドライン上で書く（1行）
行替えする代わりに、**forの行**と**doneの手前**に`;`を付けることで、1行で書くこともできます。

```sh:コマンド＆出力結果
$ for i in 1 2 3; do echo $i; done
1
2
3
```

>参考：[コマンドライン上でのbashを1行で (for文/if文)](https://qiita.com/w_atame/items/6232465079d924eae805)

以下の説明では、**2の方法**でfor文を使っていきます。
## for文内での変数の使い方
基本的にはシェルスクリプトでの変数の使い方と同じです。
「**シェルスクリプト　変数**」とかで調べるとたくさんサイトがあるので、ぜひ調べてみてください！

>・[初心者向けシェルスクリプトの基本コマンドの紹介](https://qiita.com/zayarwinttun/items/0dae4cb66d8f4bd2a337)
・[シェルスクリプトの基礎知識まとめ](https://qiita.com/katsukii/items/383b241209fe96eae6e7)

簡単にいくつかのケースを説明したいと思います。

#### 1. 「$変数名」で値を使用する
**変数 `i`** の値は、**`$i`** で使用することができます。（上の例参照）
#### 2. 変数に文字列が隣接する場合は「${変数名}」とする
**`$i`** と文字列が連続している場合には、**`${i}`**とする必要があります。

```sh
$ for i in 1 2 3
> do
> echo $ixxx
> echo ${i}xxx
> done
             ## "ixxx" という変数は存在しないので、何も出力されない。
1xxx         ## "変数iの値(=1)" + "xxx" が出力される。

2xxx

3xxx
```
#### 3. 変数は `''`ではなく`""`で囲む
```sh
ダブルクォート　　"" ... 変数の中身を読み込む（ex. "$i" -> 1）
シングルクォート　'' ... 文字列をそのまま表示（ex. '$i' ->  $i という文字列）
バッククォート　　`` ... コマンドとして読み込む（ex. `cat test.txt` -> test.txtの中身を出力）
```


Linuxでは`''` `""`に上記のような差があるので、変数をクォーテーションで囲むときは必ず**`""`を使用します**。

```sh
$ cat test01.txt
1xxxxxxxxxx
xxx2xxxxxxx
xxxxx3xxxxx
xxxxxxxx4xx
xxxxxxxxxx5

## 1 4 5 を含む行だけ抽出
$ for i in 1 4 5
> do
> cat test01.txt | grep "$i"
> done
1xxxxxxxxxx
xxxxxxxx4xx
xxxxxxxxxx5

## $i という文字列を含む行を抽出（何も出力されない）
$ for i in 1 4 5
> do
> cat test01.txt | grep '$i'
> done

```
>参考：[【bash】 「\"」、「\'」、「`」の違い - どちらかというとごはん派](https://blog.goo.ne.jp/01_mai/e/a4f9f01fb647066d0c7eb37e8ae0a254)


## 値のリストの書き方
ざっくりリストアップするとこんな感じです（他にもいろいろあると思います）。

```sh
## i を 1 2 3 4 5 で動かす
for i in 1 2 3 4 5
for i in {1..5}
for i in `seq 1 5`
for i in `seq 5`
for ((i=1; i<=5; i++))

## i を 2 ずつ動かす ( 1 3 5 7 9 )
for ((i=1; i<=10; i=i+2))         ## (最初の値; iの条件; iの増え方(or減り方))
for ((i=1; i<=10; i+=2))

## i に文字を代入
for i in aa bb cc

## ファイル内の各行を読み込む (空白でもループが区切られるので注意; 後述)
for i in `cat test.txt`

## ディレクトリ内のファイルを指定
for i in *.txt
for i in `ls`
```
>参考：
[【 for 】コマンド（応用編その2）――コマンドの実行結果と組み合わせて、繰り返し処理を行う](https://www.atmarkit.co.jp/ait/articles/1806/21/news032.html)
[【 for 】コマンド（応用編その3）――回数を指定して繰り返し処理を行う](https://www.atmarkit.co.jp/ait/articles/1806/22/news028.html)

## おまけ：ファイル内の各行を読み込む（while read line）
for文より`while`文で読み込むやり方の方がメジャーなようです（→[追記参照](#追記)）。
方法は何パターンかありますが、読み込むファイルの最終行で改行されていない場合には、方法によっては最終行だけ読み込まれない場合があるので、注意が必要です。
>参考：
[コピペ可｜bash/while read lineで行単位で処理：5パターン](https://lanchesters.site/bash-while-read-line/)
[BASHのwhile readで最終行が処理されない問題の解決方法](https://qiita.com/Ets/items/a7fa24b138b8ee883dac)

調べた限り、ヒアドキュメントで渡す方法（下の方法）ではその問題を回避できるので、個人的にはこの方法が良いのではないかと思います。

```sh
$ cat test02.txt
1
2
3
4
5    ## 改行されていない

## 最終行が読み込まれない
$ cat test02.txt | while read line
> do
> echo line : $line
> done
line : 1
line : 2
line : 3
line : 4

## 最終行まで読み込まれる
$ while read line
> do
> echo line : $line
> done << Hear
> `cat test02.txt`
> Hear
line : 1
line : 2
line : 3
line : 4
line : 5
```

### 追記
[上のリンク](https://lanchesters.site/bash-while-read-line/)の説明にもある通り、for文の場合、改行だけでなく空白でもループが区切られてしまうので、各行ごとに処理を行いたい場合には`while read line`を使った方が汎用性は高いです。

```sh
$ cat test02.txt
1 x
2xx
3
4
5

## forは空白でも区切られる
$ for i in `cat test02.txt`
> do
> echo line : $i
> done
line : 1
line : x
line : 2xx
line : 3
line : 4
line : 5

## whileは空白で区切られない
$ while read line
> do
> echo line : $line
> done << Hear
> `cat test02.txt`
> Hear
line : 1 x
line : 2xx
line : 3
line : 4
line : 5
```