Как добавить новую строку в конец файла?

Использование систем управления версиями меня раздражает шум, когда diff говорит No newline at end of file.

Итак, мне было интересно: как добавить новую строку в конце файла, чтобы избавиться от этих сообщений?

156 голосов | спросил k0pernikus 17 FebruaryEurope/MoscowbFri, 17 Feb 2012 16:44:46 +0400000000pmFri, 17 Feb 2012 16:44:46 +040012 2012, 16:44:46

20 ответов


29

Для рекурсивной дезинфекции проекта я использую этот oneliner:

git ls-files | while read f; do tail -n1 $f | read -r _ || echo >> $f; done
ответил Patrick Oscity 13 +04002014-10-13T19:28:46+04:00312014bEurope/MoscowMon, 13 Oct 2014 19:28:46 +0400 2014, 19:28:46
164

Здесь вы переходите :

sed -i -e '$a\' file

И, альтернативно, для OS X sed:

sed -i '' -e '$a\' file

Это добавляет \n в конец файла only , если он еще не заканчивается новой строкой. Поэтому, если вы запустите его дважды, он не добавит еще одну новую строку:

$ cd "$(mktemp -d)"
$ printf foo > test.txt
$ sed -e '$a\' test.txt > test-with-eol.txt
$ diff test*
1c1
< foo
\ No newline at end of file
---
> foo
$ echo $?
1
$ sed -e '$a\' test-with-eol.txt > test-still-with-one-eol.txt
$ diff test-with-eol.txt test-still-with-one-eol.txt
$ echo $?
0
ответил l0b0 17 FebruaryEurope/MoscowbFri, 17 Feb 2012 18:13:29 +0400000000pmFri, 17 Feb 2012 18:13:29 +040012 2012, 18:13:29
33

Посмотрите:

$ echo -n foo > foo 
$ cat foo
foo$
$ echo "" >> foo
$ cat foo
foo

, поэтому echo "" >> noeol-file должен сделать трюк. (Или вы хотели попросить идентифицировать эти файлы и их исправление?)

edit удалил "" из echo "" >> foo (см. комментарий @ yuyichao) edit2 снова добавлен "" (, но см. комментарий @Keith Thompson)

ответил sr_ 17 FebruaryEurope/MoscowbFri, 17 Feb 2012 16:58:17 +0400000000pmFri, 17 Feb 2012 16:58:17 +040012 2012, 16:58:17
13

Другое решение, использующее ed. Это решение влияет только на последнюю строку и только если отсутствует \n:

ed -s file <<< w

Он по существу работает над открытием файла для редактирования через скрипт, сценарий представляет собой единственную команду w, которая записывает файл обратно на диск. Он основан на этом предложении, найденном в ed(1) странице руководства:

ОГРАНИЧЕНИЯ
       (...)

       Если текстовый (небинный) файл не заканчивается символом новой строки,
       затем ed добавляет один на чтение /запись. В случае двоичного
       файл, ed не добавляет новую строку при чтении /записи.
ответил enzotib 20 FebruaryEurope/MoscowbMon, 20 Feb 2012 21:43:32 +0400000000pmMon, 20 Feb 2012 21:43:32 +040012 2012, 21:43:32
9

Простой, переносимый, совместимый с POSIX способ добавления отсутствующей, окончательной новой строки в a будет текстовым файлом:

[ -n "$(tail -c1 file)" ] && echo >> file

Этот подход не должен читать весь файл; он может просто искать EOF и работать оттуда.

Этот подход также не требует создания временных файлов за вашей спиной (например, sed -i), поэтому жесткие ссылки не затрагиваются.

echo добавляет новую строку в файл только тогда, когда результатом подстановки команды является непустая строка. Обратите внимание, что это может произойти только в том случае, если файл не пуст, а последний байт не является символом новой строки.

Если последний байт файла является новой строкой, хвост возвращает его, а затем заменяет команду; результатом является пустая строка. Тест -n терпит неудачу, и эхо не выполняется.

Если файл пуст, результатом подстановки команды также является пустая строка, и снова эхо не запускается. Это желательно, потому что пустой файл не является недопустимым текстовым файлом и не эквивалентен непустому текстовому файлу с пустой строкой.

ответил Barefoot IO 18 FebruaryEurope/MoscowbThu, 18 Feb 2016 02:30:30 +0300000000amThu, 18 Feb 2016 02:30:30 +030016 2016, 02:30:30
8

Добавьте новую строку независимо:

echo >> filename

Вот способ проверить, существует ли в конце новая строка, прежде чем добавлять ее, используя Python:

f=filename; python -c "import sys; sys.exit(open(\"$f\").read().endswith('\n'))" && echo >> $f
ответил Alexander 7 +04002013-10-07T19:18:59+04:00312013bEurope/MoscowMon, 07 Oct 2013 19:18:59 +0400 2013, 19:18:59
4

Лучше корректировать редактор пользователя, который в последний раз редактировал файл. Если вы последний человек, который отредактировал файл - какой редактор вы используете, я угадываю textmate ..?

ответил AD7six 17 FebruaryEurope/MoscowbFri, 17 Feb 2012 17:02:49 +0400000000pmFri, 17 Feb 2012 17:02:49 +040012 2012, 17:02:49
4

Самый быстрый способ проверить, является ли последний байт файла символом новой строки, - это читать только последний байт. Это можно сделать с помощью tail -c1 file. Тем не менее, упрощенный способ проверить, является ли значение байта новой строкой, в зависимости от оболочки обычное удаление завершающей новой строки внутри расширения команды не выполняется (например) в yash, когда последним символом в файле является UTF- 8.

Правильный, совместимый с POSIX, все (разумные) оболочки способ найти, является ли последний байт файла новой строкой, либо использовать xxd или hexdump:

tail -c1 file | xxd -u -p
tail -c1 file | hexdump -v -e '/1 "%02X"'

Затем сравнение результата выше с 0A обеспечит надежный тест.
Полезно избегать добавления новой строки в пустой файл.
Файл, который не сможет предоставить последний символ 0A, конечно:

f=file
a=$(tail -c1 "$f" | hexdump -v -e '/1 "%02X"')
[ -s "$f" -a "$a" != "0A" ] && echo >> "$f"

Короткий и сладкий. Это занимает очень мало времени, так как он просто считывает последний байт (ищите EOF). Не имеет значения, большой ли файл. Затем добавьте только один байт, если это необходимо.

Никаких временных файлов не требуется и не используется. Никакие жесткие ссылки не затронуты.

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

ответил sorontar 13 72016vEurope/Moscow11bEurope/MoscowSun, 13 Nov 2016 04:43:27 +0300 2016, 04:43:27
3

Самое быстрое (и лучшее) решение:

[ -n "$(tail -c1 file)" ] && print '\n' >>file 

  1. Очень быстро. Файла seq 99999999 >file, это займет миллисекунды.
    Другие решения занимают много времени:

    [ -n "$(tail -c1 file)" ] && printf '\n' >>file  0.013 sec
    vi -ecwq file                                    2.544 sec
    paste file 1<> file                             31.943 sec
    ed -s file <<< w                             1m  4.422 sec
    sed -i -e '$a\' file                         3m 20.931 sec
    
  2. Работает в ash, bash, lksh, mksh, ksh93, attsh и zsh, но не yash.

  3. Не меняет метку времени файла, если нет необходимости добавлять новую строку.
    Все остальные решения, представленные здесь, изменяют временную метку файла.
  4. Все вышеприведенные решения действительны POSIX.

Если вам требуется переносимое решение yash (и все остальные оболочки, перечисленные выше), это может немного усложниться:

f=file
if       [ "$(tail -c1 "$f"; echo x)" != "$(printf '\nx')" ]
then     printf '\n' >>"$f"
fi
ответил Isaac 2 Mayam18 2018, 06:12:25
2

Если вы просто хотите быстро добавить новую строку при обработке некоторого конвейера, используйте это:

outputting_program | { cat ; echo ; }

он также совместим с POSIX.

Затем, конечно, вы можете перенаправить его в файл.

ответил MichalH 27 J0000006Europe/Moscow 2015, 19:50:33
2

Если в вводе нет нулей:

paste - <>infile >&0

... было бы достаточно, чтобы всегда добавлять новую строку в хвост infile, если у нее ее еще не было. И нужно только прочитать входной файл за один раз, чтобы получить его правильно.

ответил 18 FebruaryEurope/MoscowbThu, 18 Feb 2016 01:39:07 +0300000000amThu, 18 Feb 2016 01:39:07 +030016 2016, 01:39:07
1

Хотя он напрямую не отвечает на вопрос, вот связанный скрипт, который я написал, чтобы обнаруживать файлы, которые не заканчиваются символом новой строки. Это очень быстро.

find . -type f | # sort |        # sort file names if you like
/usr/bin/perl -lne '
   open FH, "<", $_ or do { print " error: $_"; next };
   $pos = sysseek FH, 0, 2;                     # seek to EOF
   if (!defined $pos)     { print " error: $_"; next }
   if ($pos == 0)         { print " empty: $_"; next }
   $pos = sysseek FH, -1, 1;                    # seek to last char
   if (!defined $pos)     { print " error: $_"; next }
   $cnt = sysread FH, $c, 1;
   if (!$cnt)             { print " error: $_"; next }
   if ($c eq "\n")        { print "   EOL: $_"; next }
   else                   { print "no EOL: $_"; next }
'

Скрипт perl считывает список (необязательно отсортированных) имен файлов из stdin и для каждого файла он считывает последний символ, чтобы определить, заканчивается ли файл в новой строке или нет. Это очень быстро, потому что это позволяет избежать чтения всего содержимого каждого файла. Он выводит одну строку для каждого прочитанного файла с префиксом «error:», если возникает какая-то ошибка, «empty:», если файл пуст (не заканчивается символом новой строки!), «EOL:» («конец line "), если файл заканчивается символом новой строки и" no EOL: ", если файл не заканчивается символом новой строки.

Примечание: сценарий не обрабатывает имена файлов, которые содержат символы новой строки. Если вы работаете в Linux, вы можете обрабатывать все возможные имена файлов, добавляя -print0 для поиска, -z для сортировки и -0 для perl, например:

find . -type f -print0 | sort -z |
/usr/bin/perl -ln0e '
   open FH, "<", $_ or do { print " error: $_"; next };
   $pos = sysseek FH, 0, 2;                     # seek to EOF
   if (!defined $pos)     { print " error: $_"; next }
   if ($pos == 0)         { print " empty: $_"; next }
   $pos = sysseek FH, -1, 1;                    # seek to last char
   if (!defined $pos)     { print " error: $_"; next }
   $cnt = sysread FH, $c, 1;
   if (!$cnt)             { print " error: $_"; next }
   if ($c eq "\n")        { print "   EOL: $_"; next }
   else                   { print "no EOL: $_"; next }
'

Конечно, вам все равно придется придумать способ кодирования имен файлов с символами новой строки на выходе (слева как упражнение для читателя).

Вывод может быть отфильтрован, если необходимо, для добавления новой строки к тем файлам, у которых их нет, проще всего с

 echo >> $filename

Отсутствие окончательной новой строки может вызвать ошибки в скриптах, поскольку некоторые версии оболочки и других утилит не будут обрабатывать недостающую окончательную новую строку при чтении такого файла.

По моему опыту, отсутствие окончательной новой строки вызвано использованием различных утилит Windows для редактирования файлов. Я никогда не видел, чтобы vim вызывал отсутствующую окончательную новую строку при редактировании файла, хотя он будет сообщать о таких файлах.

Наконец, существуют гораздо более короткие (но более медленные) скрипты, которые могут перебирать входы в свои имена файлов для печати тех файлов, которые не заканчиваются в новой строке, например:

/usr/bin/perl -ne 'print "$ARGV\n" if /.\z/' -- FILE1 FILE2 ...
ответил jrw32982 26 ThuEurope/Moscow2013-12-26T22:27:46+04:00Europe/Moscow12bEurope/MoscowThu, 26 Dec 2013 22:27:46 +0400 2013, 22:27:46
1

Редакторы vi /vim /ex автоматически добавляют <EOL> в EOF, если файл уже он.

Итак, попробуйте либо:

vi -ecwq foo.txt

, что эквивалентно:

ex -cwq foo.txt

Тестирование:

$ printf foo > foo.txt && wc foo.txt
0 1 3 foo.txt
$ ex -scwq foo.txt && wc foo.txt
1 1 4 foo.txt

Чтобы исправить несколько файлов, проверьте: Как исправить «Нет новой строки в конце файла» для большого количества файлов? при SO

Почему это так важно? Чтобы сохранить наши файлы совместимы с POSIX .

ответил kenorb 16 Maypm15 2015, 16:31:18
1

Чтобы применить принятый ответ ко всем файлам в текущем каталоге (плюс подкаталоги):

$ find . -type f -exec sed -i -e '$a\' {} \;

Это работает на Linux (Ubuntu). В OS X вам, вероятно, придется использовать -i '' (untested).

ответил friederbluemle 14 J000000Tuesday15 2015, 10:53:43
0

Это работает в AIX ksh:

lastchar=`tail -c 1 *filename*`
if [ `echo "$lastchar" | wc -c` -gt "1" ]
then
    echo "/n" >> *filename*
fi

В моем случае, если в файле отсутствует строка новой строки, команда wc возвращает значение 2, и мы пишем новую строку.

ответил Daemoncan 12 FebruaryEurope/MoscowbThu, 12 Feb 2015 00:37:35 +0300000000amThu, 12 Feb 2015 00:37:35 +030015 2015, 00:37:35
0

Здесь много замечательных предложений, но одна идея - удалить новую строку и добавить ее, чтобы вы знали, что не добавляете их постоянно:

Возьмите файл «foo»:

Удалите новую строку, если она есть:

  truncate -s $(($(stat -c '%s' foo)-1)) foo

Затем добавьте один:

  sed -i -e '$a\' foo

Следовательно, foo всегда будет содержать хотя бы одну новую строку.

или зачеркните файл и найдите новую строку, если она не содержит добавить один.

  grep -q "[^0-9a-z-]" <<< $(tail -1 ./foo) && echo " " >> ./foo
ответил Mike Q 17 FebruaryEurope/MoscowbWed, 17 Feb 2016 23:51:19 +0300000000pmWed, 17 Feb 2016 23:51:19 +030016 2016, 23:51:19
0

По крайней мере, в версиях GNU просто grep '' или awk 1 канонизирует свой вход, добавляя окончательную новую строку, если она еще не присутствует , Они копируют файл в процессе, который требует времени, если большой (но источник не должен быть слишком большим для чтения в любом случае?) И обновляет время, если вы не сделаете что-то вроде

 mv file old; grep '' <old >file; touch -r old file

(хотя это может быть хорошо в файле, который вы проверяете, поскольку вы его модифицировали) и он теряет жесткие ссылки, разрешения на отказ и списки ACL и т. д., если вы еще более осторожны.

ответил dave_thompson_085 12 62016vEurope/Moscow11bEurope/MoscowSat, 12 Nov 2016 18:28:20 +0300 2016, 18:28:20
0

Добавив в ответ Патрика Осити , если вы просто хотите применить его к определенному каталогу, вы также можете Применение:

find -type f | while read f; do tail -n1 $f | read -r _ || echo >> $f; done

Запустите это в каталоге, в который вы хотите добавить новые строки.

ответил cipher 25 J000000Tuesday17 2017, 05:55:20
0

echo $'' >> <FILE_NAME> добавит пустую строку в конец файла.

echo $'\n\n' >> <FILE_NAME> добавит 3 пустых строки в конец файла.

ответил user247137 18 PM00000040000004331 2017, 16:26:43
0

Если ваш файл завершен с Windows концами строк \r\n, и вы находитесь в Linux, вы можете использовать эту команду sed. Он добавляет \r\n к последней строке, если он еще не существует:

sed -i -e '$s/\([^\r]\)$/\1\r\n/'

Пояснение:

-i    replace in place
-e    script to run
$     matches last line of a file
s     substitute
\([^\r]\)$    search the last character in the line which is not a \r
\1\r\n    replace it with itself and add \r\n

Если последняя строка уже содержит \r\n, то регулярное выражение поиска не будет соответствовать, поэтому ничего не произойдет.

ответил masgo 15 Maypm18 2018, 17:48:46

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132