How to fix “No newline at end of file” warning for lots of files?

前端 未结 11 1788
抹茶落季
抹茶落季 2020-12-03 01:48

I have a huge number of source files that are all lacking a newline at the end.

How do I automatically add a newline to the end of each of them?

Some may alr

相关标签:
11条回答
  • 2020-12-03 02:36
    pcregrep --recursive --exclude-dir=.git \
      --files-without-match --multiline '\n\z' . |
      while read k ; do echo >> "$k"; done
    

    There are several steps involved here:

    1. Recursively find files
    2. Detect which files lack a trailing new line
    3. Loop over each of those files
    4. Append the newline

    Step 1 is traditionally done with find (following the Unix tradition of "each tool doing one thing and doing it well"), but since pcregrep has builtin support, I'm comfortable using it. I'm careful to avoid messing around with the .git folder.

    Step 2 is done with a multiline regular expression matching files that do have a final newline, and printing the names of files that don't match.

    Step 3 is done with a while/read loop rather than a for/in, since the latter fails for filenames with spaces and for extremely long lists of files.

    Step 4 is a simple echo, following @norman-ramsey's approach.

    h/t @anthony-bush https://stackoverflow.com/a/20687956/577438 for the pcregrep suggestion.

    0 讨论(0)
  • 2020-12-03 02:38

    OK, after complaining in the comments, there is my better solution. First, you want to know, which files are missing newlines:

    find -type f -exec sh -c "tail -1 {} | xxd -p | tail -1 | grep -v 0a$" ';' -print
    

    Not super fast (calling a couple of processes for each file), but it's OK for practical use.

    Now, when you have it, you may as well add the newline, with another -exec:

    find -type f -exec sh -c "tail -1 {} | xxd -p | tail -1 | grep -v 0a$" ';' -exec sh -c "echo >> {}" ';'
    

    Possible gotchas:

    • if filenames are bad, e.g. they have spaces, you may need tail -1 \"{}\". Or does find do it right?

    • you may want to add more filtering to find, like -name \*py, or the like.

    • think about possible DOS/Unix newlines mess before use (fix that first).

    EDIT:

    If you don't like the output from these commands (echoing some hex), add -q to grep:

    find -type f -exec sh -c "tail -1 {} | xxd -p | tail -1 | grep -q -v 0a$" ';' -print
    find -type f -exec sh -c "tail -1 {} | xxd -p | tail -1 | grep -q -v 0a$" ';' -exec sh -c "echo >> {}" ';'
    
    0 讨论(0)
  • 2020-12-03 02:38

    I'm surprised nobody has mentioned that many simple text-processing tools like Awk will add a newline as a side effect. Here is a simple loop which will overwrite a file only if a newline was actually added.

    for f in *; do
        awk 1 "$f" >tmp
        cmp -s tmp "$f" || mv tmp "$f"
    done
    rm -f tmp
    

    (The temporary file is obviously a bit of a wart.)

    IDEone demo: http://ideone.com/HpRHcx

    0 讨论(0)
  • 2020-12-03 02:40

    If you have access to Unix tools, you can run diff to find out which files lack a newline and then append it:

    #!/bin/sh
    for i
    do
      if diff /dev/null "$i" | tail -1 | grep '^\\ No newline' > /dev/null
      then 
        echo >> "$i"
      fi
    done
    

    I'm relying on diff to produce the message with a \ in the first column, tail to give me the last line of diff's output, and grep to tell me if the last line is the message I'm looking for. If all that works, then the echo produces a newline and the >> appends it to the file "$i". The quotes around "$i" make sure things still work if the filename has spaces in it.

    0 讨论(0)
  • 2020-12-03 02:43

    Try ex-way:

    ex -s +"bufdo wq" *.c
    

    And recursively (with a new globbing option enabled):

    ex -s +"bufdo wq" **/*.c
    

    This is equivalent to vi -es. Change *.c to extension of your interest.

    The ex/vi would automatically append newline on save if it's not present.

    0 讨论(0)
提交回复
热议问题