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, 検索
-, ,

執筆者:

関連記事

VirtualBoxにUbuntu18.04をインストール(SSH接続するまで)

2019年も変わらずLinuxディストリシェアトップをキープしたUbuntuをVirtualBoxでインストールします。 マイナビニュース  7 UsersDebianとCentOS下落- …

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

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

CentOS8にPostgreSQL12をインストールする手順

以前CentOS7にPostgreSQL11をインストールしてSQL実行しました。 One IT Thing  1 Pocket開発用PostgreSQLをCentOSにインストールしてSQ …

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

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

podmanをdockerとして使う

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

 

shingo.nakanishi
 

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