Как можно объединить все строки, заканчивающиеся символом обратной косой черты?


36

Используя общий инструмент командной строки, такой как sed или awk, возможно ли объединить все строки, заканчивающиеся заданным символом, например, обратную косую черту?

Например, учитывая файл:

foo bar \
bash \
baz
dude \
happy

Я хотел бы получить этот вывод:

foo bar bash baz
dude happy

1
Передайте файл через cpp:)
imz - Иван Захарящев

Столько чудесных ответов, я бы хотел отметить их все как ответ! Спасибо за отличный взгляд на awk, sed и perl, это были отличные примеры.
Кори Кляйн

Обратите внимание, что это в sedFAQ
Стефан Шазелас

Ответы:


27

более короткое и простое решение sed:

sed  '
: again
/\\$/ {
    N
    s/\\\n//
    t again
}
' textfile

или однострочник при использовании GNU sed:

sed ':x; /\\$/ { N; s/\\\n//; tx }' textfile

1
хороший ... я изначально смотрел на это и не мог понять (так что это не было в слишком трудной корзине) ... но после всестороннего взгляда на ответ Жиля (который занял довольно много времени) Я еще раз взглянул на ваш ответ, и он выглядел удивительно понятным. Думаю, я начинаю понимать sed:) ... Вы добавляете каждую строку непосредственно в пространство шаблонов, и когда появляется строка с "нормально законченным", все пространство шаблона падает и автоматически печатает (потому что нет опции -n) ... аккуратно! .. +1
Питер

@fred: спасибо, я думаю, я тоже начинаю понимать sed, он предлагает хорошие инструменты для многострочного редактирования, но как их перепутать, чтобы получить то, что вам нужно, не просто и читаемость не на высоте ...
neurino

Остерегайтесь окончания строки DOS, иначе. возврат каретки или \ r!
user77376

1
Что не так сsed -e :a -e '/\\$/N; s/\\\n//; ta'
Исаак

18

Возможно, это проще всего с perl (поскольку perl похож на sed и awk, я надеюсь, что он приемлем для вас):

perl -p -e 's/\\\n//'

Короче говоря, мне нравится этот +1. И он явно не просил sed или awk
rudolfson

17

Вот решение awk. Если строка заканчивается на \, удалите обратную косую черту и напечатайте строку без завершающей строки; в противном случае выведите строку с завершающим символом новой строки.

awk '{if (sub(/\\$/,"")) printf "%s", $0; else print $0}'

Это также не так уж плохо в sed, хотя awk явно более читабелен.


2

Это не ответ как таковой. Это побочный вопрос о sed.

В частности, мне нужно было sedразбирать команду Жиля на части по частям, чтобы понять это ... Я начал писать некоторые заметки об этом, а затем подумал, что это может быть кому-то здесь полезно ...

так вот оно ... Сценарий Жиля Седа в документированном формате:


#!/bin/bash
#######################################
sed_dat="$HOME/ztest.dat"
while IFS= read -r line ;do echo "$line" ;done <<'END_DAT' >"$sed_dat"
foo bar \
bash \
baz
dude \
happy
yabba dabba 
doo
END_DAT

#######################################
sedexec="$HOME/ztest.sed"
while IFS= read -r line ;do echo "$line" ;done <<'END-SED' >"$sedexec"; \
sed  -nf "$sedexec" "$sed_dat"

  s/\\$//        # If a line has trailing '\', remove the '\'
                 #    
  t'Hold-append' # branch: Branch conditionally to the label 'Hold-append'
                 #         The condition is that a replacement was made.
                 #         The current pattern-space had a trailing '\' which  
                 #         was replaced, so branch to 'Hold-apend' and append 
                 #         the now-truncated line to the hold-space
                 #
                 # This branching occurs for each (successive) such line. 
                 #
                 # PS. The 't' command may be so named because it means 'on true' 
                 #     (I'm not sure about this, but the shoe fits)  
                 #
                 # Note: Appending to the hold-space introduces a leading '\n'   
                 #       delimiter for each appended line
                 #  
                 #   eg. compare the hex dump of the follow 4 example commands:  
                 #       'x' swaps the hold and patten spaces
                 #
                 #       echo -n "a" |sed -ne         'p' |xxd -p  ## 61 
                 #       echo -n "a" |sed -ne     'H;x;p' |xxd -p  ## 0a61
                 #       echo -n "a" |sed -ne   'H;H;x;p' |xxd -p  ## 0a610a61
                 #       echo -n "a" |sed -ne 'H;H;H;x;p' |xxd -p  ## 0a610a610a61

   # No replacement was made above, so the current pattern-space
   #   (input line) has a "normal" ending.

   x             # Swap the pattern-space (the just-read "normal" line)
                 #   with the hold-space. The hold-space holds the accumulation
                 #   of appended  "stripped-of-backslah" lines

   G             # The pattern-space now holds zero to many "stripped-of-backslah" lines
                 #   each of which has a preceding '\n'
                 # The 'G' command Gets the Hold-space and appends it to 
                 #   the pattern-space. This append action introduces another
                 #   '\n' delimiter to the pattern space. 

   s/\n//g       # Remove all '\n' newlines from the pattern-space

   p             # Print the pattern-space

   s/.*//        # Now we need to remove all data from the pattern-space
                 # This is done as a means to remove data from the hold-space 
                 #  (there is no way to directly remove data from the hold-space)

   x             # Swap the no-data pattern space with the hold-space
                 # This leaves the hold-space re-initialized to empty...
                 # The current pattern-space will be overwritten by the next line-read

   b             # Everything is ready for the next line-read. It is time to make 
                 # an unconditional branch  the to end of process for this line
                 #  ie. skip any remaining logic, read the next line and start the process again.

  :'Hold-append' # The ':' (colon) indicates a label.. 
                 # A label is the target of the 2 branch commands, 'b' and 't'
                 # A label can be a single letter (it is often 'a')
                 # Note;  'b' can be used without a label as seen in the previous command 

    H            # Append the pattern to the hold buffer
                 # The pattern is prefixed with a '\n' before it is appended

END-SED
#######

1
Решение Нейрино на самом деле довольно просто. Говоря о слегка сложном седе, это может вас заинтересовать .
Жиль "ТАК - перестань быть злым"

2

Еще один распространенный инструмент командной строки ed, который по умолчанию изменяет файлы на месте и, следовательно, оставляет права доступа к файлам без изменений (дополнительную информацию edсм. В разделе « Редактирование файлов с помощью редактора ed текста из сценариев» ).

str='
foo bar \
bash 1 \
bash 2 \
bash 3 \
bash 4 \
baz
dude \
happy
xxx
vvv 1 \
vvv 2 \
CCC
'

# We are using (1,$)g/re/command-list and (.,.+1)j to join lines ending with a '\'
# ?? repeats the last regex search.
# replace ',p' with 'wq' to edit files in-place
# (using Bash and FreeBSD ed on Mac OS X)
cat <<-'EOF' | ed -s <(printf '%s' "$str")
H
,g/\\$/s///\
.,.+1j\
??s///\
.,.+1j
,p
EOF

2

Используя тот факт, что readв оболочке будут интерпретировать обратные слеши при использовании без -r:

$ while IFS= read line; do printf '%s\n' "$line"; done <file
foo bar bash baz
dude happy

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


Нет. Это не удалит весь обратный слеш. Попробуйте сa\\b\\\\\\\\\\\c
Исаак

@ Исаак Ах, может быть, мне следовало сказать «интерпретировать любой другой обратный слеш»?
Кусалананда

1

Простое (r) решение, которое загружает весь файл в память:

sed -z 's/\\\n//g' file                   # GNU sed 4.2.2+.

Или еще короткая, которая понимает (выводит) строки (синтаксис GNU):

sed ':x;/\\$/{N;bx};s/\\\n//g' file

В одну строку (синтаксис POSIX):

sed -e :x -e '/\\$/{N;bx' -e '}' -e 's/\\\n//g' file

Или используйте awk (если файл слишком большой, чтобы поместиться в памяти):

awk '{a=sub(/\\$/,"");printf("%s%s",$0,a?"":RS)}' file

0

Версия для Mac на основе решения @Giles будет выглядеть так

sed ':x
/\\$/{N; s|\\'$'\\n||; tx
}' textfile

Где главное различие заключается в том, как представляются новые строки, и объединение любого дальнейшего в одну строку разбивает его


-1

Вы можете использовать cpp, но он генерирует несколько пустых строк, где объединяет вывод, и некоторое введение, которое я удаляю с помощью sed - возможно, это можно сделать с помощью cpp-flags и параметров:

echo 'foo bar \
bash \
baz
dude \
happy' | cpp | sed 's/# 1 .*//;/^$/d'
foo bar bash baz
dude happy

Вы уверены , что cpp это решение? В вашем примере echoстрока с двойными кавычками уже выводит выпрямленный текст, поэтому cppбессмысленно. (Это также относится к вашему sedкоду.) Если вы помещаете строку в одинарные кавычки, cppпросто удаляет обратную косую черту, но не объединяет строки. (Конкатенация с cppбудет работать, если не будет пробела перед
обратными слешами

@manatwork: Аутч! :) Я был удивлен, что команда sed сработала, но, конечно, это была не команда sed, но сам bash интерпретирует обратную косую черту как продолжение предыдущей строки.
неизвестный пользователь

Использование cppэтого до сих пор не объединяет строки для меня. И использование sedопределенно не нужно. Использование cpp -P: « -PЗапретить генерацию линейных маркеров в выходных данных препроцессора». - man cpp
manatwork

Ваша команда не работает для меня: cpp: “-P: No such file or directory cpp: warning: '-x c' after last input file has no effect cpp: unrecognized option '-P:' cpp: no input filesА cpp --versionпоказывает cpp (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3- что? Ubuntu исправляет cpp? Зачем? Я бы ожидал прочитать GNU ...
пользователь неизвестен

Интересный. Ubuntu cppдействительно объединяет строки и оставляет некоторые пробелы. Еще интереснее то, что здесь принимает ту же версию 4.4.3-4ubuntu5.1 -P. Однако это только устраняет маркеры строки, пустые строки остаются.
manatwork
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.