Удалить символ новой строки только каждые N строк

16

Обрабатывая текст, мне нужно удалять символ новой строки каждые две строки.

Образец текста:

this is line one
and this is line two
the third and the
fourth must be pasted too

Желаемый вывод:

this is line one and this is line two
the third and the fourth must be pasted too

Я пробовал whileцикл, но цикл while - плохая практика. Можно ли сделать это с помощью trили любой другой командой?

jomaweb
источник
4
В заголовке написано «каждые N строк», но в вопросе и примере «каждые 2 строки». Большинство ответов работают только для N = 2. Вы ищете что-то, что работает для всех N?
JigglyNaga
Это ключ. Все отвечали на две строки, но мне нужно было бы использовать N = 3 или N = 4
jomaweb

Ответы:

24

paste(также стандартная простая утилита POSIX tr) - ваш инструмент для этого.

Предполагая, что вы хотите, чтобы эти символы новой строки были заменены пробелом, а не просто удалены, как в вашем примере:

paste -d ' ' - - < file

Или:

paste -sd ' \n' file

Замените ' 'на, '\0'если вы действительно хотите, чтобы они были удалены.

Чтобы заменить 2 из 3:

paste -sd '  \n' file

1 из 3, начиная со второго:

paste -sd '\n \n' file

И так далее.

Еще одна хорошая вещь paste- это то, что она не оставит строку без завершения. Например, если вы удаляете каждую новую строку в файле (как с помощью tr -d '\n' < fileили tr '\n' ' ' < file), вы в итоге не получаете никакой строки, поскольку строки должны заканчиваться символом новой строки. Поэтому, как правило, лучше использовать pasteвместо этого (как в paste -sd '\0' fileили paste -sd ' ' file), который добавит завершающий символ новой строки, необходимый для правильного текста.

Стефан Шазелас
источник
11

С современным GNU SED

sed -rz 's/\n([^\n]*\n)/ \1/g' sample.text

И awk

awk '{getline line2;print $0, line2}' sample.text
Костас
источник
3
Этот sedподход подразумевает удаление всего файла в памяти (при условии, что он не содержит байтов NUL) и выполнение дорогостоящей замены регулярных выражений. Я не вижу преимущества по сравнению со стандартным sed 'N;s/\n/ /'подходом.
Стефан Шазелас
6

Используйте sedдля этого, как показано ниже:

SHW@SHW:/tmp $ cat a
this is line one
and this is line two
the third and the
fourth must be pasted too

SHW@SHW:/tmp $ sed 'N;s/\n/ /' a -i

SHW@SHW:/tmp $ cat a
this is line one and this is line two
the third and the fourth must be pasted too
SHW
источник
4

Другой способ заключается в использовании xargs:

$ < txt xargs -d '\n' -n 2 echo
this is line one and this is line two
the third and the fourth must be pasted too

где

$ cat txt
this is line one
and this is line two
the third and the
fourth must be pasted too

Хотя это решение является чрезмерным, потому что echoпроцесс выполняется для каждой строки ... Таким образом, помимо примеров игрушек, решение на основе awk / sed или аналогичного должно быть предпочтительным.

maxschlepzig
источник
1
В зависимости от вашей echoреализации у вас также могут возникнуть проблемы с символами обратной косой черты или некоторыми строками, начинающимися с -(например, --helpили -neneс GNU echo). Также обратите внимание, что -dэто расширение GNU.
Стефан Шазелас
Чтобы избежать проблем echo, вы можете использовать это:< txt xargs -d '\n' -n 2 printf -- '%s %s\n'
nyuszika7h
4

На самом деле это очень просто в vim. Чтобы присоединиться к каждой строке, используйте Jкоманду, затем используйте %normкоманду, чтобы применить ее к каждой строке одновременно. Например

:%norm J<CR>

(На тот случай, если вы не знакомы с vim, <CR>просто означает войти)

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

:%norm 9J<CR>

Если вас не устраивает vim, и вы предпочитаете использовать его в качестве инструмента командной строки, а не интерактивного текстового редактора, вы можете сделать следующее:

vim myfile -c '%norm J' -c 'wq'
DJMcMayhem
источник
Не мог бы даунвотер объяснить, что я могу сделать, чтобы улучшить этот ответ?
DJMcMayhem
3
$ awk '{printf "%s%s",$0,(NR%2?" ":"\n")}' sample.txt
this is line one and this is line two
the third and the fourth must be pasted too

Это выводит на печать каждую строку, $0сопровождаемую либо пробелом, либо переводом строки в зависимости от того, является ли номер строки NRнечетным или четным.

Выражение NR%2?" ":"\n"является троичным утверждением. Выражение NR%2оценивается как истинное (ненулевое), если номер строки нечетный. В этом случае троичное выражение возвращает пробел. Если оно оценивается как ложное (ноль), тогда возвращается символ новой строки.

альтернатива

Как предложено Костасом в комментариях:

$ awk '{ORS=(NR%2?" ":RS)}1' sample.txt
this is line one and this is line two
the third and the fourth must be pasted too

Здесь троичный оператор NR%2?" ":RSиспользуется для возврата пробела или разделителя входных записей (по RSумолчанию = новая строка). Это значение присваивается разделителю выходной записи ORS. В 1конце команды - загадочное сокращение awk для print-the-record.

John1024
источник
Вы все еще можете сохранить 3 символа: ()круглые скобки и пробел после printf;)
maxschlepzig
1
Троичный? Ой! 'NR%2{printf("%s ",$0);next}1'
Костас
С ответом maxschlepzig и троичным утверждением:'{ORS=(NR%2?" ":RS)}1'
Костас
@ Костас, мне это нравится. Ответ дополнен ORSрешением.
John1024
2

Типовое решение, замените 5на необходимое количество строк

$ # eof to ensure last line has newline ending
$ seq 16 | perl -pe 's/\n/ / if ++$i%5 && !eof'
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16

$ # or just use pr
$ seq 16 | pr -5ats' '
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16
Sundeep
источник
1

Вы можете использовать awkдля этого:

$ awk '{c="\n"} NR%2 {c=" "} { printf("%s%s", $0, c) } ' txt

Это производит:

this is line one and this is line two
the third and the fourth must be pasted too

где:

$ cat txt
this is line one
and this is line two
the third and the
fourth must be pasted too

Эти awkдействия выполняются для каждой строки, специальные переменные $0ссылки на текущую строку, NRэто номер текущей строки (начиная с 1). Второе действие защищается выражением NR%2, которое является операцией по модулю. Таким образом, c=" "выполняется только в том случае, если NR%2истина, т.е. для нечетных номеров строк.

awkСинтаксис C , как, но некоторые элементы не являются обязательными в некоторых контекстах - например , точка с запятой.

maxschlepzig
источник
Ваша cпеременная ORS:'NR%2{ORS=" "}1;{ORS=RS}'
Костас
0

Использование ed:

$ cat text
this is line one
and this is line two
the third and the
fourth must be pasted too
this is line one
and this is line two
the third and the
fourth must be pasted too

$ ed text <<'END_ED'
g/./s/$/ /\
j
w text.new
END_ED
164
164

$ cat text.new
this is line one and this is line two
the third and the fourth must be pasted too
this is line one and this is line two
the third and the fourth must be pasted too

В edкомандах редактирования будет, для каждой строки ( gприменяется набор команд редактирования для каждой строки , соответствующей заданного регулярного выражение), добавьте символ пробела до конца и присоединиться к нему со следующей строкой. Затем он записывает полученный текст в файл с именем text.new.

Кусалананда
источник
0

С рубином.

Я предполагаю, что каждый блок nстрок должен быть объединен. Предположим n = 3, входной файл 'infile'и результаты должны быть записаны в файл 'outfile'.

Построить файл

Ruby -e "File.write 'infile', <<_
> Line 1
> Line 2
> Line 3
> Line 4
> Line 5
> Line 6
> Line 7
> _"

Подтвердите содержимое файла

ruby -e "p File.read 'infile'"
  # "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\n"

Удалить переводы строки и записать в файл

ruby -e "File.write 'outfile', File.readlines('infile').
  each_with_index { |line,i| line.chomp! unless (i+1)%3==0 }"

Подтвердите содержание

ruby -e "puts File.read 'outfile'"
  # ["Line 1", "Line 2", "Line 3\n", "Line 4", "Line 5", "Line 6\n", "Line 7"]
Кэри Свовеланд
источник
1
Хороший. В теории rubyэто не по теме U & L. Но, так как вы используете его из командной строки ruby -e, этого достаточно по теме.
Grochmal