One IT Thing

IT業界を楽しむ為の学習系雑記

linux 検索

テキストファイル内の改行コード差異を見つけるワンライナー

投稿日:2019年8月22日 更新日:

こんなケースを想定。

  • ファイル内容は同じなのにdiffで違いが出る
  • Web上でソースレビューする時に同じ内容の行なのに差分が出て紛らわしい

これらの原因はWindows、Mac、Linuxが入り混じった開発環境にあります。

find ./ -type f  | xargs -I{} -t awk '{if($0 ~ /\r$/) {print NR, $0}}' {}

を実行すればCRLFが混じったファイルを行番号付きで特定できます。CI中にこのコマンドを流して、意図しない改行コードが含まれるファイルがあったらチャットに警告を流す、とかするとホスピタリティ高めの開発環境になるかも知れません。

以下本文は補足です。

環境

  • CentOS 7.6

OSによる改行コードの違い

テキストファイルでEnterを押して行が次に行くのは、行末に以下の制御コードが入る為。(OSによって改行コードは異なる)

OSchar数値
WindowsCRLF(\r\n)0x0D(13) 0x0A(10)
Linux、MacOS XLF(\n)0x0A(10)
MacOS 9以前CR(\r)0x0D(13)

MacOS 9はなくなって久しく、現代の改行コードはCRLF、LFの違いのみです。

ASCIIコード128バイトの内、0~9(0x30~0x39)、a~z(0x61~0x7A)のようなテキスト化出来るASCIIコード以外は制御コードとして存在します。

以下はman asciiの結果。0x0D(13)はCR(復帰)、0x0A(10)LF(改行)であることが分かります。

Oct   Dec   Hex   Char                        Oct   Dec   Hex   Char
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
000   0     00    NUL '\0'                    100   64    40    @
001   1     01    SOH (ヘッダ開始)            101   65    41    A
002   2     02    STX (テキスト開始)          102   66    42    B
003   3     03    ETX (テキスト終了)          103   67    43    C
004   4     04    EOT (転送終了)              104   68    44    D
005   5     05    ENQ (問い合わせ)            105   69    45    E
006   6     06    ACK (肯定応答)              106   70    46    F
007   7     07    BEL '\a' (ベル)             107   71    47    G
010   8     08    BS  '\b' (バックスペース)   110   72    48    H
011   9     09    HT  '\t' (水平タブ)         111   73    49    I
012   10    0A    LF  '\n' (改行)             112   74    4A    J
013   11    0B    VT  '\v' (垂直タブ)         113   75    4B    K
014   12    0C    FF  '\f' (改ページ)         114   76    4C    L
015   13    0D    CR  '\r' (復帰)             115   77    4D    M
016   14    0E    SO  (シフトアウト)          116   78    4E    N
017   15    0F    SI  (シフトイン)            117   79    4F    O
020   16    10    DLE (伝送制御拡張)          120   80    50    P
021   17    11    DC1 (装置制御1)             121   81    51    Q
022   18    12    DC2 (装置制御2)             122   82    52    R
023   19    13    DC3 (装置制御3)             123   83    53    S
024   20    14    DC4 (装置制御4)             124   84    54    T
025   21    15    NAK (否定応答)              125   85    55    U
026   22    16    SYN (同期)                  126   86    56    V
027   23    17    ETB (転送ブロック終了)      127   87    57    W
030   24    18    CAN (キャンセル)            130   88    58    X
031   25    19    EM  (メディア終了)          131   89    59    Y
032   26    1A    SUB (置換)                  132   90    5A    Z
033   27    1B    ESC (エスケープ)            133   91    5B    [
034   28    1C    FS  (ファイル区切り)        134   92    5C    \  '\\'
035   29    1D    GS  (グループ区切り)        135   93    5D    ]
036   30    1E    RS  (レコード区切り)        136   94    5E    ^
037   31    1F    US  (ユニット区切り)        137   95    5F    _
040   32    20    SPACE                       140   96    60    `
041   33    21    !                           141   97    61    a
042   34    22    "                           142   98    62    b
043   35    23    #                           143   99    63    c
044   36    24    $                           144   100   64    d
045   37    25    %                           145   101   65    e
046   38    26    &                           146   102   66    f
047   39    27    ´                           147   103   67    g
050   40    28    (                           150   104   68    h
051   41    29    )                           151   105   69    i
052   42    2A    *                           152   106   6A    j
053   43    2B    +                           153   107   6B    k
054   44    2C    ,                           154   108   6C    l
055   45    2D    -                           155   109   6D    m
056   46    2E    .                           156   110   6E    n
057   47    2F    /                           157   111   6F    o

060   48    30    0                           160   112   70    p
061   49    31    1                           161   113   71    q
062   50    32    2                           162   114   72    r
063   51    33    3                           163   115   73    s
064   52    34    4                           164   116   74    t
065   53    35    5                           165   117   75    u
066   54    36    6                           166   118   76    v
067   55    37    7                           167   119   77    w
070   56    38    8                           170   120   78    x
071   57    39    9                           171   121   79    y
072   58    3A    :                           172   122   7A    z
073   59    3B    ;                           173   123   7B    {
074   60    3C    <                           174   124   7C    |
075   61    3D    =                           175   125   7D    }
076   62    3E    >                           176   126   7E    ~
077   63    3F    ?                           177   127   7F    DEL

3パターンのファイルを作成してdiffしてみる

  1. 改行がCRLFのみのファイル(crlf.txt、windowsのterapadで作成)
  2. 改行がLFのみのファイル(lf.txt、viで作成)
  3. 改行がCRLF、LFが混じったファイル(crlf-and-lf.txt、echo >> dddd crlf.txt)

改行が見えるようにcatで内容を確認。(-eは行末(LF)を”$”で置き換え)

$ cat -e crlf.txt
aaaa^M$  <-- CRLF
bbbb^M$
cccc^M$
dddd^M$

$ cat -e lf.txt
aaaa$    <-- LF
bbbb$
cccc$
dddd$

$ cat -e crlf-and-lf.txt
aaaa^M$  <-- CRLF
bbbb^M$
cccc^M$
dddd$    <-- LF

ファイル内容が同じでも改行コードが違えば差分が出る。

$ diff crlf.txt lf.txt
1,4c1,4
< aaaa
< bbbb
< cccc
< dddd
---
> aaaa
> bbbb
> cccc
> dddd

ありがちなのが開発チームメンバーのPCがWindowsとMacOSXで混在しており、CRLFとLFが混じってしまうケース。

$ diff crlf.txt crlf-and-lf.txt
4c4
< dddd
---
> dddd

同じ内容の行なのに差分が出て煩わしいですね。

CRLF行を持つファイルがあるか再帰的に調べる

コマンドで改行コードの違いを見つけられるようにしておきます。

  1. findで見つけたカレント配下のファイルをxargsで一ファイルずつawkに渡す
  2. awkは引数({})で渡されたファイルを一行ずつ舐める
  3. CRで終わる行を探し、行番号(NR)と行全体($0)をコンソールに表示
$ find ./ -name "*.txt" \
    | xargs -I{} -t awk '{if($0 ~ /\r$/) {print NR, $0}}' {}

awk {if($0 ~ /\r$/) {print NR, $0}} ./crlf.txt
1 aaaa
2 bbbb
3 cccc
4 dddd
awk {if($0 ~ /\r$/) {print NR, $0}} ./lf.txt
awk {if($0 ~ /\r$/) {print NR, $0}} ./crlf-and-lf.txt
1 aaaa
2 bbbb
3 cccc

CRLFが改行コードになっているファイルが行番号付きで表示出来ました。

逆にLFで終わっているファイル行を出したい場合、awkの条件式を以下に変えます。

$ find ./ -type f \
    | xargs -I{} -t awk '{if($0 !~ /\r$/) {print NR, $0}}' {}

awk {if($0 !~ /\r$/) {print NR, $0}} ./crlf.txt
awk {if($0 !~ /\r$/) {print NR, $0}} ./lf.txt
1 aaaa
2 bbbb
3 cccc
4 dddd
awk {if($0 !~ /\r$/) {print NR, $0}} ./crlf-and-lf.txt
4 dddd

見つけた後は統一されるように変更すればいいですね。

IDEで統一しておけば混乱は起きない

そもそも最初からIDEで統一しておけば差分に悩まされずに済みます。

visual studio code

「ファイル」→「基本設定」→「改行」検索。「規定の改行文字」を設定。
今後新規作成するファイルはこの改行コードになります。

ただし既存ファイルは元のまま。既存ファイルの改行コードを変更するには右下の改行コードをクリック。

Eclipse

「ウィンドウ」→「設定」→「一般」→「ワークスペース」。「新規テキスト・ファイルの行区切り文字」を設定。

既存ファイルの改行コードを変更するには「ファイル」→「行区切り文字の変換」を実行。

IDEで改行コードを統一出来ました。

まとめ

perlが全盛期の時代は改行コードの違いでプログラムが動かなくなったりして神経質になったものでした。

今は動かなくなるようなことは無いにせよ、オンラインソースレビューで差分が出てしまったり何気にいやらしいデメリットが発生します。

プロジェクトを始める際に改行コードはコレで。と決めておけばこんな問題とは無縁でいられますね。

-linux, 検索
-, ,

執筆者:

関連記事

OpenGrokをインストールしてソースリーディング環境を作る

職場でもプライベートでも「あのフレームワークのあの処理はどういうコードになってるのか」気になってソースを落としてくることがあるかと思います。 職場や自宅ならIDEにソースを入れてビルドしてソースを追え …

Termuxで動作が確認できたターミナルゲーム4つ

Android端末をroot化せずにLinux環境を独自構築する神アプリ「Termux」。  play.google.comTermux – Apps on Google Playhttps …

CentOS8をVirtualBoxにインストールしてXからdnfを打つまで

2019年9月24日(米時間)CentOS8がリリースされました。 RHEL8リリースから4か月、まだかまだかとリリース進捗を眺めていた方も多かったのではないでしょうか。 「時間が出来たら入れてみよう …

universal-ctagsをソースからコンパイルしてCentOS7にインストール

ctagsはプログラムソースを読みこませてクラス名、メソッド名、変数名他各種識別子をインデックスしたtagsファイルを作成するプログラムツールです。 tagsファイルをvimやemacsなどの外部プロ …

Linuxのシェルログイン時、牛に格言を喋らせる

sshコマンドやteratermでLinuxにログインした時、在り来たりなプロンプトじゃなくて何か役に立つ情報が表示されたら、開発プロジェクトの雰囲気もちょっと潤うかも知れません。 Linuxコマンド …


shingo nakanishi。東京で消耗中の職歴20年越え中年ITエンジニアです。「生涯現役プログラマを楽しむ」ことができる働き方探しをライフワークにしています。

19歳(1996年)から書き始めた個人日記が5,000日を超え、残りの人生は発信をして行きたいと思い、令和元日からこのサイトを開始しました。勉強と試行錯誤をしながら、自分が経験したIT関連情報を投稿しています。