LoginSignup
19
14

More than 5 years have passed since last update.

ファイルの末が改行 LF であるか判定する Bash スクリプト

Last updated at Posted at 2015-05-28

ファイルの最後の文字が改行なら 0 で終了、それ以外なら 1 で終了する Bash スクリプト。

かなりちまちましているけど、ファイルの末尾については重要な問題だと思っている。

今回は Bash のスクリプトで書いてしまったが、できれば POSIX 原理主義的なやり方も知りたいと思う。

The Open Group Base Specifications Issue 7

The Open Group Base Specifications Issue 7 によると

3.397 Text File

A file that contains characters organized into one or more lines.

3.206 Line

A sequence of zero or more non- <newline> characters plus a terminating <newline> character.

どうやらテキストファイルとは「1 つ以上の行」行は「0 個以上の改行以外の文字と末尾の改行」らしい。

このように末尾が改行で終わっているか判定し、正常なら 0 、それ以外なら 0 以外 で終了コードを返すスクリプトが欲しくなった。

もちろん、「この仕様に基づいた上でのテキストファイルとは」という話なだけなので、
私が「この世の全てのテキストファイルとはかくあるべし」と主張しているわけではない。
いろいろな OS に、それぞれの仕様があると思う。

書いた

endswith_linebreak
#!/usr/bin/env bash
exec tail -c 1 | cmp -s - <(echo)

使い方

終了コードだけではコンソールからは判別つかないので、 echo "$?" をつけて例示してみる。
調べたいファイルを標準入力にする。

$ ./endswith_linebreak < target_file ; echo "$?"
0

なお Bash 特有の機能である、コマンドを <() でリダイレクトする機能を使っている。
ので、このスクリプトは POSIX 準拠というわけではない。無念。

これではダメだった

最初、もっとシンプルに次のようなスクリプトを考えていた。

test "$(tail -c 1 "$1")" = $'\n'

残念ながら、これでは意図通りの動作にならない。
コマンド置換は末尾の改行が潰されてしまうのだ。そのため常に終了コードは 1 になる。

これは普段

$ echo "$(pwd)/somefile"

と実行したとき

$ echo "$(pwd)/somefile"
/foo/bar
somefile

とならずに

$ echo "$(pwd)/somefile"
/foo/bar/somefile

となることからも理解できる。

今回はそこでいろいろと苦心したものの、結局思い浮かばなかった。
Bash スクリプトで割りきって書くことにした。
また、せっかくなのでファイルを引数で指定するのではなく標準入力を使うことにした。

もし標準入力ではなく引数でファイルを指定する形式なら

endswith_linebreak
#!/usr/bin/env bash
exec cmp -s <(tail -c 1 "$1") <(echo)

と書けばいい。

2015-05-30 追記:

コメント欄にて tail -n 1 | wc -l すると良いとのことを教えていただいたので更新。

endswith_linebreak
#!/bin/sh
set -eu
PATH='/bin:/usr/bin'
LANG='C'
exec test "$(tail -n 1 "$1" | wc -l)" -eq 1
19
14
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
14