ShellScriptにおいて，テキストファイル末尾に改行を加える．

# 要約
　テキストファイル末尾に改行を加える方法として，1）最終行に改行がない場合に改行を加える方法と，2）空行を加える方法とがある．前者には文字列操作系のコマンド（grep，sed，awk，他）とechoを用いる方法がある．後者にはechoを用いる．よくわからないときはechoで空行を加えて，後に不要な空行を削除すればよい．

# はじめに

　テキストファイルは文末が改行で終わることもあるし，終わらないこともある．そして，シェルスクリプトでテキストファイルを操作する際，文末の改行有無で不具合を生ずることがある．したがって，文末に改行をいれたり，逆に削除したりする操作は重要な技術といえる[[1]]．そのためか，文章末尾に改行を加える方法[[2]][[3]]，あるいは文末の改行を削除する方法がしばしば議論される[[4]][[5]]．複数の方法があり意外に複雑である．ここでは，テキストファイル末尾に改行を加える方法を整理することにした．

# 環境
MacOS: 10.15.3
zsh: 5.7.1
bash: 3.2.57

# 準備

### 定義（行，不完全な行，テキストファイル）

　<font color="red">行の最後には改行があるのが基本である</font>．その理由はPOSIX（Portable Operating System Interface）[[6]]における，"行 Line"，"不完全な行 Incomplete Line"，"文章ファイル Text File"の定義からわかる[[7]][[8]]．
</br>
</br>
 
- 行（Line）[[9]]

行の定義によると，行は改行で終わるものである．

>行とは，"改行"以外の0個以上の一連の文字列で，"改行"記号で終わる．  
>A sequence of zero or more non-newline characters plus a terminating <newline> character.
</br>
</br>

- 不完全な行（Incomplete Line）[[10]]

文末の最後の行が改行で終わらない場合，その最後の行は不完全な行といえる．

>不完全な行とは，ファイル末尾における"改行"以外の1個以上の一連の文字列．
>A sequence of one or more non-newline characters at the end of the file.
</br>
</br>

文章ファイル（Text File）[[11]]

文章ファイルは複数の行の集合体といえる[[1]]．

>文章ファイルとは，0個以上の行からなる（以下略）．
>A file that contains characters organized into zero or more lines. The lines do not contain NUL characters and none can exceed {LINE_MAX} bytes in length, including the newline character. Although POSIX.1-2017 does not distinguish between text files and binary files (see the ISO C standard), many utilities only produce predictable or meaningful output when operating on text files. The standard utilities that have such restrictions always specify "text files" in their STDIN or INPUT FILES sections.


　上記定義を踏まえ，以下のように言葉を（一応）統一する．

- **テキストファイル**: 複数の行の集合体．
- **テキストもしくは文章**: テキストファイルの内容．
- **テキスト末尾もしくは文章末尾**: テキストファイルの最後の文字．
- **行**: 改行で終える行．
- **行末尾**: 行の最後の文字．
- **不完全な行**: 改行で終えない行．

</br>
</br>

### テキストファイルを準備．

- 検証のため以下の2つのファイル作成する．
- 文章の末尾に改行がない，すなわち最終行が改行で終わらないテキストファイル（file_1.txt）．
- 文章最後に改行がある，すなわち最終行が改行で終わるテキストファイル（file_2.txt）．
- 以下に，catコマンドで上記2つのファイルの内容を表示する．

```zsh:file_1.txtの内容を示す．
% cat file_1.txt
A
B
C
D
E
F  注）zshでは F% と表示される（注意点参照）．          
```

```zsh:file_2.txtの内容を示す．
% cat file_2.txt
g h
i j
k l
m n


```
</br>

<font color="red">**注意点**</font>
　zshでは最終行が改行されていない場合，自動的に改行され，その際に勝手に改行したことを示す記号として"反転色の%"が出力される[[12]][[13]][[14]]．


### 末尾（$）を可視化．

- 適宜検証するため，行末尾を可視化する．
- 正規表現において末尾を示すダラー（$）を表示させる．
- `cat`コマンドの`-e`オプションを利用する．

```zsh:file_1.txt，最終行に$がついていない．
% cat -e file_1.txt
A$
B$
C$
D$
E$
F
```
```zsh:file_2.txt，最下の空行にも改行あり．

% cat -e file_2.txt
g h$
i j$
k l$
m n$
$
$
```


### 改行（\n）を可視化．

- 適宜検証するため，改行を可視化する．
- `od -c`を利用する．

```zsh:file_1.txt，Fの後に改行がないことがわかる．
% od -c file_1.txt
0000000    A  \n   B  \n   C  \n   D  \n   E  \n   F
0000013
```

### 文字数，行数などを確認する．

- 適宜検証するため，文字数などを確認する．
- `wc`コマンドにオプション`-c`（バイト数），`-m`（文字数），`-l`（行数）をつけて確認する．

```zsh:file_1.txt，最後の行は改行していないため，行として数えられていない．
% cat file_1.txt | wc -l
       5
```
```zsh:file_1.txt，改行も一文字として数えられている．
% cat file_1.txt | wc -c
      11
```

# テキストファイルの最後の行の末尾に改行がない場合，改行を加える．

- テキストファイル末尾に改行がない場合に加える方法を紹介する．
- すなわち，テキストファイル末尾に改行がある場合は，下記の方法ではなにも起きない．


## 文字列操作系（検索，置換，抽出）のコマンドを用いる方法．

- 標準入力を受け，操作を加え，標準出力するコマンド（grep，sed，awk他）は，標準出力する際， "不完全な行" に自動的に改行を加えてくれることが多い．それを利用する．
- コマンドは内容を明示するため，なるべくシングルクオートではさむ方針とする．
- これらの難点は，コードの目的が "テキストファイル末尾に改行を加える" とわかりにくい点である．
- また，環境によって挙動も異なると思われる（未検証）．

### sedを用いる方法．
- sedは左から受ける場合，コマンド不要．
- 右からふける場合に同様に`sed ''`とするとエラーとなる．そこでプリント`p`のみするようにする．その際に，自動的に入力が表示され，二重で表示されるのを防ぐため，`-n`オプションをつける．

```zsh:sed左から受ける．
% cat file_1.txt | sed ''
# シングルクオートなしで　cat file_1.txt | sed でも可．
```

```zsh:sed右から受ける．
% sed -n 'p' file_1.txt
# シングルクオートなしで　sed -n p file_1.txt　でも可．
```

</br>

### awkを用いる方法．

- awkにおいて，パターンに`1`がくると真になり，暗黙のアクションである '{print $0}' が実行される[[16]][[17]]．

```zsh:awk左から受ける．
% cat file_1.txt | awk '1'
# シングルクオートなしで　cat file_1.txt | awk 1　でも可．
```
```zsh:awk右から受ける．
% awk '1' file_1.txt
#　シングルクオートなしで　awk 1 file_1.txt　でも可．
```
</br>

### grepを用いる方法．
- 以下は`cat file_1.txt | grep '^.*$'`でも同じ結果．

```zsh:grep左から受ける．
% cat file_1.txt | grep ''
#　これはシングルクオートなしだとうまくいかない．
```
```zsh:grep右から受ける．
% grep '' file_1.txt
#　これはシングルクオートなしだとうまくいかない．
```

</br>

### revを用いる方法．

- 反転して反転すると改行が付く．

```zsh
% cat file_1.txt | rev | rev
# 他に　rev file_1.txt | rev　も可．
```

### echoを用いた方法[[18]]．
- `echo`は末尾に改行が勝手にはいる．それを利用する．
- echo `cat file_1.txt` をバッククオートではさみ，さらにダブルクオートではさむと，改行もそのままで標準出力される．
- echo `''` は不要かもしれないが，いれてもうまくいく．

```zsh
% echo "`cat file_1.txt`"
# もしくは，echo "`cat file_1.txt`"''
```


## 他のコマンド
- `paste file_1.txt` でもできた．

</br>
</br>

# テキストファイル末尾に空行を加える．

- 前節は最後の行が改行で終わっていない場合に改行を加える方法である．
- テキストファイル末尾に空行を追加する方法も併せて述べる．


### ファイルに直接，改行を追加する方法[[3]]
- リダイレクト`>>`で追加[[15]]．
- `echo`は自動的に最後に改行が加わる．

```
% echo "" >> testA.txt
```

### 標準出力に改行を追加する方法．
- echoを用いる．
- 下記は冗長だが，コードの意味は比較的分かり易いと思う．
- この方法であれば，`'\n'`を繰り返せば，いくらでも空行を加えることができる．

```zsh
% theTpm=$(cat file_1.txt)| echo $theTmp'\n'

```

- `-n`オプションで自動で改行をしないようにし，そのあと自分で`\n`を加えている．

```zsh
% echo -n "`cat file_1.txt`"'\n''\n'
```
</br>

（追記）
### <( Angle Parentheses )
[[19]][[20]]

```zsh
% cat file_1.txt <(echo '')
# この方法も幾つでも空行を追加できる．
```


# 考察

- POSIXでは行は改行で終えるのが前提であることを考えると，テキストファイル末尾に改行を追加することの方が，テキストファイル末尾の改行を削除することよりも多いと思う．
- 改行を加える操作は意外に複雑で面倒．
- 一方，空行を削除するのは簡単（例えば `sed '/^$/d'` でできる）．
- よくわからないときは，最後に紹介した標準出力に空行を加える方法を用いて，あとで空行を削除すればよい．

# まとめ

　テキストファイル末尾に改行を加える方法を述べた．


# 参考
\[1]:https://zariganitosh.hatenablog.jp/entry/20131216/end_of_file_lf_control</br>
ファイル末尾の改行を自在にコントロールする - ザリガニが見ていた...。</br>
\[2]:*https://stackoverflow.com/questions/23055831/add-new-line-character-at-the-end-of-file-in-bash</br>
Add new line character at the end of file in bash - Stack Overflow</br>
\[3]:https://codeday.me/jp/qa/20190316/423833.html</br>
echo "" >> file.txt : bashのファイルの最後に改行文字を追加する - コードログ</br>
\[4]:https://unix.stackexchange.com/questions/254644/how-do-i-remove-newline-character-at-the-end-of-file</br>
shell script - How do I remove newline character at the end of file? - Unix & Linux Stack Exchange</br>
\[5]:https://qiita.com/euxn23/items/f7c60999a121f906ccdb
shell の出力の末尾の改行を取り除く - Qiita</br>
\[6]:http://e-words.jp/w/POSIX.html
POSIX（Portable Operating System Interface）とは - IT用語辞典 e-Words
\[7]:https://bottoms-programming.com/archives/indent-tab-space-and-new-line.html
「行末改行って必要？」「インデントのタブとスペースってどっちがいいの？」とかのコード規約の話 | とむじそブログ
\[8]:https://stackoverflow.com/questions/729692/why-should-text-files-end-with-a-newline
unix - Why should text files end with a newline? - Stack Overflow
\[9]:https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206
Definitions
\[10]:https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_195
Definitions
\[11]:https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_403
Definitions
\[12]:https://qiita.com/2357gi/items/6d530820402ae776ca66
zshの出力の最後に追記される謎の文字の話(パーセント) - Qiita
\[13]:https://stackoverflow.com/questions/36977990/why-zsh-adds-at-the-end-of-my-output
iterm2 - why zsh adds "%" at the end of my output - Stack Overflow
\[14]:https://unix.stackexchange.com/questions/167582/why-zsh-ends-a-line-with-a-highlighted-percent-symbol
bash - Why ZSH ends a line with a highlighted percent symbol? - Unix & Linux Stack Exchange
\[15]:https://orebibou.com/2016/02/%E8%A6%9A%E3%81%88%E3%81%A6%E3%82%8B%E3%81%A8%E6%A1%88%E5%A4%96%E4%BE%BF%E5%88%A9%E3%81%AAbash%E3%81%AE%E3%83%AA%E3%83%80%E3%82%A4%E3%83%AC%E3%82%AF%E3%83%88%E3%83%BB%E3%83%91%E3%82%A4%E3%83%97/
覚えてると案外便利なBashのリダイレクト・パイプの使い方9個 | 俺的備忘録 〜なんかいろいろ〜
\[16]:https://qiita.com/b4b4r07/items/45d34a434f05aa896d69
実用 awk ワンライナー - Qiita
\[17]:https://qiita.com/ngyuki/items/b55ce646cd84eb71c900
複数のファイルを終端に改行がなければ改行を追加して結合する - Qiita
\[18]:http://sekitaka-1214.hatenablog.com/entry/2015/01/08/231918
2つのファイルの内容を改行を挟んで連結するbash - ほげほげ
\[19]:https://fj.hatenablog.jp/entry/2016/03/06/170907
Bashの括弧 - 超ウィザード級ハッカーのたのしみ
\[20]:https://dev.to/rpalo/bash-brackets-quick-reference-4eh6/comments
Discussion of Bash Brackets Quick Reference — DEV


[1]:https://zariganitosh.hatenablog.jp/entry/20131216/end_of_file_lf_control
[2]:https://stackoverflow.com/questions/23055831/add-new-line-character-at-the-end-of-file-in-bash
[3]:https://codeday.me/jp/qa/20190316/423833.html
[4]:https://unix.stackexchange.com/questions/254644/how-do-i-remove-newline-character-at-the-end-of-file
[5]:https://qiita.com/euxn23/items/f7c60999a121f906ccdb
[6]:http://e-words.jp/w/POSIX.html
[7]:https://bottoms-programming.com/archives/indent-tab-space-and-new-line.html
[8]:https://stackoverflow.com/questions/729692/why-should-text-files-end-with-a-newline
[9]:https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206
[10]:https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_195
[11]:https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_403
[12]:https://qiita.com/2357gi/items/6d530820402ae776ca66
[13]:https://stackoverflow.com/questions/36977990/why-zsh-adds-at-the-end-of-my-output
[14]:https://unix.stackexchange.com/questions/167582/why-zsh-ends-a-line-with-a-highlighted-percent-symbol
[15]:https://orebibou.com/2016/02/%E8%A6%9A%E3%81%88%E3%81%A6%E3%82%8B%E3%81%A8%E6%A1%88%E5%A4%96%E4%BE%BF%E5%88%A9%E3%81%AAbash%E3%81%AE%E3%83%AA%E3%83%80%E3%82%A4%E3%83%AC%E3%82%AF%E3%83%88%E3%83%BB%E3%83%91%E3%82%A4%E3%83%97/
[16]:https://qiita.com/b4b4r07/items/45d34a434f05aa896d69
[17]:https://qiita.com/ngyuki/items/b55ce646cd84eb71c900
[18]:http://sekitaka-1214.hatenablog.com/entry/2015/01/08/231918
[19]:https://fj.hatenablog.jp/entry/2016/03/06/170907
[20]:https://dev.to/rpalo/bash-brackets-quick-reference-4eh6/comments