Как исправить предупреждение "нет новой строки в конце файла" для многих файлов?
У меня есть огромное количество исходных файлов, которым не хватает новой строки в конце.
Как автоматически добавить строку в конец каждого из них?
некоторые могут уже иметь новую строку, поэтому ее следует добавлять только при необходимости.
Я, вероятно, не ищу код, как таковой, но просто что-то, что я могу запустить в терминале, чтобы добавить необходимые новые строки (или какой-то инструмент программирования или разработки).
11 ответов
преобразованный ответ Нормана к разделенному ОДН-вкладышу для удобства.
for i in * ; do  echo $i; \
 if diff /dev/null "$i" | tail -1 | \
  grep '^\ No newline' > /dev/null; then echo >> "$i"; \
 fi; done
заменить * на любой шаблон файла, который вы хотите, например *.c
и другой, чтобы просто сказать вам, какие файлы сломаны:
for i in * ; do \
 if diff /dev/null "$i" | tail -1 | \
  grep '^\ No newline' > /dev/null; then  echo $i; \
 fi; done
если у вас есть доступ к инструментам Unix, вы можете запустить diff чтобы узнать, в каких файлах отсутствует новая строка, а затем добавьте ее:
#!/bin/sh
for i
do
  if diff /dev/null "$i" | tail -1 | grep '^\ No newline' > /dev/null
  then 
    echo >> "$i"
  fi
done
Я полагаюсь на diff чтобы создать сообщение с \ в первом столбце, tail чтобы дать мне последнюю строку и grep чтобы сказать мне, является ли последняя строка сообщением, которое я ищу.  Если все это работает, то echo производит новую строку и >> добавляет его в файл "$i".  В кавычки "$i" убедитесь, что все еще работает, если в имени файла есть пробелы.
ОК, после жалобы в комментариях, есть мое лучшее решение. Во-первых, вы хотите знать, в каких файлах отсутствуют новые строки:
find -type f -exec sh -c "tail -1 {} | xxd -p | tail -1 | grep -v 0a$" ';' -print
Не очень быстро (вызов нескольких процессов для каждого файла), но это нормально для практического использования.
теперь, когда у вас есть это, вы можете также добавить новую строку, с другим -exec:
find -type f -exec sh -c "tail -1 {} | xxd -p | tail -1 | grep -v 0a$" ';' -exec sh -c "echo >> {}" ';'
возможные gotchas:
- если имена файлов плохие, например, у них есть пробелы, вам может понадобиться - tail -1 \"{}\". Или find делает это правильно?
- вы можете добавить больше фильтрации, чтобы найти, как - -name \*py, или тому подобное.
- подумайте о возможном беспорядке DOS/Unix newlines перед использованием (сначала исправьте это). 
EDIT:
Если вам не нравится вывод этих команд (Эхо некоторых шестнадцатеричных), добавьте -q для поиска:
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 >> {}" ';'
простое исправление для файлов, которые "отсутствуют" новой строки в конце файла просто sed; следующие исправления файла " на месте "(с помощью опции "-i"):
find . -type f -exec sed -i -e '$a\' {} \; -print 
пояснение: найти все файлы (-type f), run sed изменить файлы на месте (-i), С учетом следующих (-e) скрипт/выражение, которое соответствует концу файла ($) и выполните действие" добавить" (a\), но на самом деле не указывайте текст для добавления (ничего после \), которая собирается добавить новую строку в конец файла, но только если он отсутствует. Печатает все найденные файлы (исправленные или нет), что, вероятно, не нужно. 
главное предостережение в том, что sed функции различаются на разных платформах, поэтому -i и -e может или не может поддерживаться / то же самое; например, более старые Unix или macOS странности могут требовать немного другого синтаксиса.
find -type f | while read f; do [[ `tail -c1 "$f"` ]] && echo >> "$f"; done
Я использую find вместо for f in * как это рекурсивно, и вопрос был о "огромном количестве исходных файлов".
Я использую while read вместо find -exec или xargs по соображениям производительности он экономит процесс нереста оболочки каждый раз.
Я использую тот факт, что оператор backtick возвращает вывод команды "с любыми удаленными конечными линиями"man bash, поэтому для правильно завершенных файлов backtick будет пустым и эхо будет пропущено.
на find | read пара не будет работать на имена файлов, которые содержат новые строки, но это легко исправить, если требуется:
find -type f -print0 | while read -d $'' f; do [[ `tail -c1 "$f"` ]] && echo >> "$f"; done
попробуйте ex-way:
ex -s +"bufdo wq" *.c
и рекурсивно (с новая опция globbing включен):
ex -s +"bufdo wq" **/*.c
это эквивалентно vi -es. Изменить *.c для расширения вашего интереса.
на ex/vi автоматически добавит новую строку при сохранении, если ее нет.
из-за локализации команды Тим и Норман ответ должны быть улучшены с помощью префикса "LANG=C", чтобы иметь возможность соответствовать шаблону "No newline" с каждой системой, имеющей любые региональные параметры
Это гарантирует окончание пустой строки для каждого файла, помещенного в командной строке этого скрипта:
 #!/bin/sh -f
 for i in $* ; do  echo $i; \
 if LANG=C diff /dev/null "$i" | tail -1 | \
  grep '^\ No newline' > /dev/null; then echo >> "$i"; \
 fi; done
и этот скрипт обнаруживает файлы, в которых его нет:
 #!/bin/sh -f
 for i in $* ; do \
 if LANG=C diff /dev/null "$i" | tail -1 | \
  grep '^\ No newline' > /dev/null; then  echo $i; \
 fi; done
после поиска инструмента выполните эту работу без везения. Я решаю написать свой собственный
Это мой скрипт python для выполнения этой работы
Он только добавляет (\r\n) к файлу, не содержащему (\n) в конце файла
https://github.com/tranhuanltv/append_newline
использование: append_newline.py .с. /проекты. /result_dir
сделайте запросы Pull, если вы хотите
Я удивлен, что никто не упомянул, что многие простые инструменты обработки текста, такие как Awk, добавят новую строку в качестве побочного эффекта. Вот простой цикл, который перезапишет файл, только если новая строка была фактически добавлена.
for f in *; do
    awk 1 "$f" >tmp
    cmp -s tmp "$f" || mv tmp "$f"
done
rm -f tmp
(временный файл, очевидно, немного бородавка.)
IDEone демо: http://ideone.com/HpRHcx
pcregrep --recursive --exclude-dir=.git \
  --files-without-match --multiline '\n\z' . |
  while read k ; do echo >> "$k"; done
здесь задействовано несколько шагов:
- рекурсивно находит файлы
- определить, какие файлы не имеют конечной новой строки
- цикл над каждым из этих файлов
- добавить строку
 Шаг 1 традиционно выполняется с помощью find (следуя традиции Unix 
"каждый инструмент делает одну вещь и делает это хорошо"), но поскольку pcregrep имеет встроенную поддержку, мне удобно его использовать.  Я стараюсь избегать неприятностей. вокруг .папка git.
Шаг 2 выполняется с помощью многострочных регулярных выражений с файлами do есть последняя новая строка и печать имен файлов, которые не матч.
Шаг 3 выполняется с циклом while/read, а не for/in, поскольку последний не работает для имен файлов с пробелами и для очень длинных списков файлов.
Шаг 4-это простое Эхо, следующее подходу @ norman-ramsey.
h/t @anthony-bush https://stackoverflow.com/a/20687956/577438 для предложения pcregrep.
ниже мое решение bash-скрипт. Сначала он проверяет, является ли файл текстовым. Затем, если это текстовый файл, он использует tail и od (восьмеричный дамп), чтобы увидеть, является ли последний символ символом новой строки. Если это не так, то он добавляет новую строку с помощью эхо:
item=""
if file "$item" | egrep '\btext\b' > /dev/null
then
    if ! tail -c 1 "$item" | od -b -A n | egrep '\b012\b' > /dev/null
    then
        echo "(appending final newline to ${item})"
        echo >> "$item"
    fi
fi
