テキストデータ末尾の改行 \n を削除（および追加、判定、カウント）

こんにちは。
テキストデータ末尾（およびテキストファイル末尾）の改行 ```\n```[^1]を削除[^2]（およびその逆となる追加）の方法を調べました。

### データ末尾の ```\n``` を一個を削除 ###

データを ```head -c -1``` へ渡せば末尾に ```\n``` が存在すれば一個が削除されます。GNU head を利用しています[^3]。

[^1]: 本稿は、最終行末尾の改行 ```\n``` についての話題です（行末毎の改行ではありません）。
[^2]: ただし、Unix (POSIX) の原則論ではテキストデータ（およびテキストファイル）の最終行末尾に改行 ```\n``` を付けることになります。したがって削除が必要となる場合は多くないだろうと思います
[^3]: macOS では ```homebrew``` を用いてインストール可能。

#### wc -l コマンドで確認 ####

その確認方法例として、```wc -l``` は全ての ```\n``` をカウントしますので、一個減ったことが確認できます。

```console
$ brew install coreutils
$ echo -ne 'aa\nbb\n' | wc -l
       2
$ echo -ne 'aa\nbb\n' | ghead -c -1 | wc -l
       1
```

#### 用途例 ####

利用例として、末尾の改行を削除し空要素が現れないようにしてから、```tr```、```jq```コマンドへ渡しました。

```console
$ echo -ne 'aa\nbb\n' | ghead -c -1 | tr '\n' '/' | awk 1
aa/bb
$ echo -ne 'aa\nbb\n' | ghead -c -1 | jq -scR 'split("\n")'
["aa","bb"]
```

##### jq コマンドで空要素を削除 #####

なお、```jq```コマンドでは```select```コマンドを利用して空要素を削除できます。

```console
$ echo -ne 'aa\nbb\n' | jq -scR 'split("\n") | map(select(. != ""))'
["aa","bb"]
$ echo -ne 'aa\nbb\n' | jq -scR 'split("\n") | map(select(length > 0))'
["aa","bb"]
```

### データ末尾が ```\n``` で終わるように必要ならば追加 ###

上記とは逆に、もしも最終行が ```\n``` で終わっていなくとも ```awk '{print}'```  に渡せば受け取ってくれ[^4]、その ```print``` が ```\n``` を末尾に追加して出力します。もしくは ```grep ^``` へ渡しても同様です[^5]。

```read``` コマンドなどは ```\n``` で終わっている行を読み取った時のみに正常な終了ステータスとなるので、前もってこのような追加が必要となります。

[^4]: もしくは ```print``` を省略しても ```awk``` の引数へパターン条件の真値を与えるだけでも同じことになります（すなわち non-zero 数値（例えば ```awk 1```)、もしくは non-empty 文字列（例えば ```'" "'```） を与える）。参考例は、「[複数のファイルを終端に改行がなければ改行を追加して結合する](https://qiita.com/ngyuki/items/b55ce646cd84eb71c900)」
[^5]: ```grep``` を含むいくつかコマンドは、最終行が ```\n``` で終わっていなくとも受け取ってくれ、 その場合は ```\n``` を末尾に追加して出力します。ただし実装依存のようで、例えば GNU sed は追加しません。

```console
$ echo -ne 'aa\nbb' | grep ^ | while read line; do echo $line; done
aa
bb
$
$ echo -ne '' | grep ^ | wc -l
       0
$ echo -ne 'aa\nbb' | grep ^ | wc -l
       2
$ echo -ne 'aa\nbb\n' | grep ^ | wc -l
       2
$ echo -ne 'aa\nbb\n\n' | grep ^ | wc -l
       3
```

なお行数カウントが目的ならば、```grep -c ^ ``` でも同等となります。

```console
$ echo -ne '' | grep -c ^
0
$ echo -ne 'aa\nbb' | grep -c ^ 
2
$ echo -ne 'aa\nbb\n' | grep -c ^ 
2
$ echo -ne 'aa\nbb\n\n' | grep -c ^ 
3
```

### データ末尾の ```\n``` の存在を判定 ###

tail コマンドを利用し ```tail -c 1``` により末尾の 1 バイトを抜き出せます。したがって、それが ```\n``` なのかを判定することができます。下記例は ```wc -l``` でカウントして確認しています（なお ```tail -n 1``` により最終行を抜き出しても同じとなります）。

```console
$ echo -ne 'aa\nbb\n' | tail -c 1 | wc -l
       1
$ echo -ne 'aa\nbb\n' | ghead -c -1 | tail -c 1 | wc -l
       0
```

#### 判定方法 ####
また、より判定に向いた方法は、```tail -c 1``` の出力を ```"$(...)"``` に入れて末尾の ```\n``` を取り除き、空文字となるかを判定する方法があります（```test -z``` コマンドを利用）。

```console
$ [ -z "$(echo -ne 'aa\nbb\n' | tail -c 1)" ]; echo $?
0
$ [ -z "$(echo -ne 'aa\nbb\n' | ghead -c -1 | tail -c 1)" ]; echo $?
1
```

これは下記のような利用例となります。

```console
$ filename="example.txt"
$ [ -z "$(tail -c 1 $filename)" ] && echo "$filename: 末尾は改行です／もしくは中身が空です" || echo "$filename: 末尾は改行ではありません" 
example.txt: 末尾は改行です／もしくは中身が空です
```

中身が空（= 0 行）かどうかの判定は、
```console
$ wc -c empty.txt | awk '{print $1}'
0
```

### 末尾の改行の個数をカウント ###
テキストデータ末尾の連続した改行 ```\n``` の個数を求めるシェルスクリプトを作りました。

```console
$ echo -ne '\n\n' | ./count.sh 
2
$ echo -ne '\nA\nA' | ./count.sh 
0
```

```count.sh
#!/bin/sh
n=0
while read -r line; do
  [ -n "${line}" ] && n=0
  n=$((n + 1))
done
[ -n "${line}" ] && n=0
echo "$n"
```

----------------

下記の方法も参考になりました。
* 「[ファイル末尾の改行を自在にコントロールする](http://d.hatena.ne.jp/zariganitosh/20131216/end_of_file_lf_control)」（ザリガニが見ていた...。）
* "[Removing a newline character at the end of a file](https://stackoverflow.com/questions/16365155/removing-a-newline-character-at-the-end-of-a-file)" (Stack Overflow)