Как исправить предупреждение "нет новой строки в конце файла" для многих файлов?
У меня есть огромное количество исходных файлов, которым не хватает новой строки в конце.
Как автоматически добавить строку в конец каждого из них?
некоторые могут уже иметь новую строку, поэтому ее следует добавлять только при необходимости.
Я, вероятно, не ищу код, как таковой, но просто что-то, что я могу запустить в терминале, чтобы добавить необходимые новые строки (или какой-то инструмент программирования или разработки).
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