Как исправить предупреждение "нет новой строки в конце файла" для многих файлов?

У меня есть огромное количество исходных файлов, которым не хватает новой строки в конце.

Как автоматически добавить строку в конец каждого из них?

некоторые могут уже иметь новую строку, поэтому ее следует добавлять только при необходимости.

Я, вероятно, не ищу код, как таковой, но просто что-то, что я могу запустить в терминале, чтобы добавить необходимые новые строки (или какой-то инструмент программирования или разработки).

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. рекурсивно находит файлы
  2. определить, какие файлы не имеют конечной новой строки
  3. цикл над каждым из этих файлов
  4. добавить строку

Шаг 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