1

I have a Git project that contains a lot of files with no trailing newline. I want to add trailing newlines without adding superfluous newlines. How can I do this?

Zaz
  • 46,476
  • 14
  • 84
  • 101
  • Does this answer your question? [Add a newline only if it doesn't exist](https://stackoverflow.com/questions/10082204/add-a-newline-only-if-it-doesnt-exist) – tripleee Nov 09 '22 at 14:25
  • That question is informative, but addresses the case of only one file. – Zaz Nov 10 '22 at 02:04
  • Trivially, add [Execute command on all files in a directory](https://stackoverflow.com/questions/10523415/execute-command-on-all-files-in-a-directory) – tripleee Nov 10 '22 at 05:07

1 Answers1

2

I found this surprisingly tricky to do using the tools I would usually use (grep, sed), but an elegant solution does exist using standard shell commands:

tail -c 1 file.txt | read || echo >> file.txt    
  1. tail Outputs the last byte of file
  2. read Reads a line into a variable. With no variable specified, does nothing, but if an EOF occurs before a newline, exits with code 1.
  3. echo Runs only if read fails (i.e. if the last character was not a newline), and appends a newline to file.txt

To find the files we want to change you can use find:

find -not -path "./.git/*" -type f -exec sh -c "grep -Iq . {} && (tail -c 1 {} | read || echo >> {})" \;
  1. -not -path excludes .git/, which we don't want to mess with
  2. -type f restricts the search to files
  3. -exec sh -c "..." is required to bundle together commands that include pipes
  4. grep -Iq . searches for anything (.) so is a no-op, but exits with code 1 if file is binary
  5. {} marks the position where find will insert the file name

I would advise checking the list of files before running the command. Do this by replacing echo >> {} with echo {}:

find -not -path "./.git/*" -type f -exec sh -c "grep -Iq . {} && (tail -c 1 {} | read || echo {})" \;
Zaz
  • 46,476
  • 14
  • 84
  • 101