Як можна поєднати всі рядки, які закінчуються символом зворотної косої риси?


36

Використовуючи загальний інструмент командного рядка, як sed або awk, чи можна з'єднати всі рядки, які закінчуються заданим символом, як, наприклад, зворотна косої риски?

Наприклад, з урахуванням файлу:

foo bar \
bash \
baz
dude \
happy

Я хотів би отримати такий результат:

foo bar bash baz
dude happy

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

Стільки чудових відповідей, я б хотів, щоб я міг позначити їх усіх як відповідь! Дякую за чудовий погляд на awk, sed та perl, це були чудові приклади.
Cory Klein

Зауважте, що це у sedFAQ
Stéphane Chazelas

Відповіді:


27

коротше і простіше рішення sed:

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

або однолінійний, якщо використовується GNU sed:

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

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

@fred: спасибі, я думаю, я теж починаю розуміти sed, він пропонує приємні інструменти для редагування рядків, але як їх змішати, щоб отримати те, що вам потрібно, - це не просто, а читання - вгорі ...
neurino

Остерігайтеся закінчень рядків DOS, ака. карета повертається або!
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

Ось дивне рішення. Якщо рядок закінчується \символом a , накресліть зворотний проріз і надрукуйте рядок без закінчення нового рядка; інакше надрукуйте рядок із закінченням нового рядка.

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
Рішення Нейріно насправді досить просте. Якщо говорити про м'яко складний sed, то це може вас зацікавити .
Жил "ТАК - перестань бути злим"

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
Ісаак

@Isaac Ага, можливо, я мав би сказати "інтерпретувати будь-яку іншу косу рису"?
Кусалаланда

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-прапорами та параметрами:

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: Outsch! :) Я був здивований, що команда sed працювала, але, звичайно, це була не команда sed, але сам bash інтерпретує зворотну косу рису як продовження попереднього рядка.
користувач невідомий

Таке використання cppвсе ще не поєднує рядки для мене. І використання sed, безумовно, зайве. Використання cpp -P: « -PInhibit покоління linemarkers у виході з препроцесора.» - чоловік каст
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 filesA cpp --versionвиявляє cpp (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3- що? Ubuntu виправляє cpp? Чому? Я б сподівався прочитати GNU ...
користувач невідомий

Цікаво. Ubuntu cppдійсно об'єднує лінії та залишає деякі пробіли. Ще цікавіше, що тут приймається та ж версія 4.4.3-4ubuntu5.1 -P. Однак це лише усуває лінійні маркери, порожні рядки залишаються.
манастирство
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.