git linux 検索

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

投稿日:2019年8月22日

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

これらの原因はWindows、Mac、Linuxが入り混じった、改行コードが統一されていない開発環境にあるかも知れません。

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

を実行すれば、意図せず全行がCRLFに置換されてしまった、或いはCRLFが混じってしまったファイルを行番号付きで特定できます。

環境

  • CentOS 7.6

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

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

OSchar数値
WindowsCRLF(\r\n)0x0D(13) 0x0A(10)
Linux、MacOS XLF(\n)0x0A(10)
MacOS 9以前(10以降はLF)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はxargs引数{}で渡されたファイルを一行ずつ舐める(その際LFはawkが削除済み)
  3. (LFは削除されているので)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

見つけた後は統一されるように変更すればいいですね。手作業で変換する場合は

sed -i 's/\r//g' CRLFが含まれたファイル

などでCRを削除、LFのみに変更します。

-git, linux, 検索
-, ,

執筆者:

関連記事

podmanをdockerとして使う

docker代替としてRHEL系OSにデフォルト搭載されたpodmanは当初、dockerを期待している他周辺ソフトウェア(docker-componseやgitlab-runnerなど)と素直に通信 …

Linuxコマンドラインでfortuneを日本語翻訳してcowsayに日本語を喋らせる

緊張感が漂うことも多々あるIT開発現場、逼迫した状況が続く際は一服の潤いも必要です。以前、Linuxログインした時にcowsayにfotuneを喋らせる伝統芸能で嗜みました。 One IT Thing …

無料で自由に使える日本全国の企業データベースをPostgreSQLで作る

開発するシステムよっては一覧から企業を選択する機能があるかも知れません。「企業データベース」でググると沢山の商用企業データベースが出てきます。 「でも・・・お高いんでしょう?」 いいえ、なんと今なら・ …

Android版ChromiumをVirtualBoxにインストールしたUbuntuでビルドしてみる

「Chromium」はChromeの開発ソースが公開されたプロジェクトです。 www.chromium.org  1 UserGet the Code: Checkout, Build, & …

GitLabのIPを変更したらMattermostにログイン出来なくなった話「the redirect uri included is not valid. 」

目次1 発端2 環境3 GitLabのIP変更設定を開始4 そしてハマる5 解決6 結局どこに設定が保存されていたか7 まとめ 発端 先日オフィスの引っ越しが有りネットワークセグメントが変わる為、Gi …

 

shingo.nakanishi
 

東京在勤、1977年生まれ、IT職歴2n年、生涯技術者として楽しく生きることを目指しています。デスマに負けず健康第一。