Bagaimana Anda bisa menggabungkan semua garis yang berakhir dengan karakter garis miring terbalik?

36

Menggunakan alat baris perintah umum seperti sed atau awk, apakah mungkin untuk bergabung dengan semua baris yang diakhiri dengan karakter yang diberikan, seperti backslash?

Misalnya, diberikan file:

foo bar \
bash \
baz
dude \
happy

Saya ingin mendapatkan hasil ini:

foo bar bash baz
dude happy
Cory Klein
sumber
1
Lewati file melalui cpp:)
imz - Ivan Zakharyaschev
Begitu banyak jawaban yang indah, saya berharap saya bisa menandai semuanya sebagai jawaban! Terima kasih atas tampilan hebat pada awk, sed, dan perl, ini adalah contoh yang bagus.
Cory Klein
Perhatikan bahwa ini ada di sedFAQ
Stéphane Chazelas

Jawaban:

27

solusi sed yang lebih pendek dan sederhana:

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

atau satu-liner jika menggunakan GNU sed:

sed ':x; /\\$/ { N; s/\\\n//; tx }' textfile
neurino
sumber
1
bagus ... saya secara initally melihat ini dan tidak bisa memahaminya (jadi tidak masuk ke keranjang yang terlalu sulit) ... tapi setelah melihat jawaban Gilles secara mendalam (yang butuh waktu lama) Saya melihat lagi jawaban Anda dan itu terlihat sangat bisa dimengerti. Saya pikir saya mulai mengerti sed:) ... Anda menambahkan setiap baris langsung ke pola-ruang, dan ketika garis "biasanya berakhir" muncul, maka seluruh ruang pola jatuh dan cetakan otomatis (karena tidak ada opsi -n) ... rapi! .. +1
Peter.O
@ fred: terima kasih saya pikir saya mulai mengerti juga, ia menawarkan alat yang bagus untuk pengeditan multiline tapi bagaimana mencampurkannya untuk mendapatkan apa yang Anda butuhkan tidak mudah atau mudah dibaca di atas ...
neurino
Waspadai akhiran garis DOS, alias. carriage return or \ r!
user77376
1
Apa yang salah dengansed -e :a -e '/\\$/N; s/\\\n//; ta'
Isaac
18

Ini mungkin termudah dengan perl (karena perl seperti sed dan awk, saya harap ini dapat diterima oleh Anda):

perl -p -e 's/\\\n//'
camh
sumber
pendek dan sederhana, saya suka yang +1 Dan dia tidak meminta sed atau
canggung
2

Ini bukan jawaban. Ini adalah masalah sampingan tentang sed.

Secara khusus, saya perlu mengambil sedperintah Gilles terpisah satu demi satu untuk memahaminya ... Saya mulai menulis beberapa catatan di atasnya, dan kemudian berpikir itu mungkin berguna di sini untuk seseorang ...

jadi ini dia ... Script sed Gilles dalam format yang terdokumentasi :


#!/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
#######
Peter.O
sumber
1
Solusi Neurino sebenarnya cukup sederhana. Berbicara tentang sed yang agak rumit, ini mungkin menarik bagi Anda .
Gilles 'SO- stop being evil'
2

Namun alat baris perintah umum lainnya adalah ed, yang secara default memodifikasi file di tempat dan karenanya membiarkan izin file tidak dimodifikasi (untuk informasi lebih lanjut tentang edmelihat Mengedit file dengan editor teks ed dari skrip )

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
verdo
sumber
2

Menggunakan fakta bahwa readdi shell akan mengartikan garis miring terbalik ketika digunakan tanpa -r:

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

Perhatikan bahwa ini juga akan menafsirkan backslash lain dalam data.

Kusalananda
sumber
Nggak. Itu tidak akan menghapus semua backslash. Coba dengana\\b\\\\\\\\\\\c
Isaac
@ Isaac Ah, mungkin saya seharusnya mengatakan "menafsirkan backslash lainnya"?
Kusalananda
1

Solusi sederhana (r) yang memuat seluruh file dalam memori:

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

Atau yang masih pendek yang berfungsi memahami baris (keluaran) (sintaksis GNU):

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

Pada satu baris (sintaks POSIX):

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

Atau gunakan awk (jika file terlalu besar untuk muat di memori):

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

Versi Mac berdasarkan solusi @Giles akan terlihat seperti ini

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

Di mana perbedaan utama adalah bagaimana baris baru diwakili, dan menggabungkan lebih jauh ke dalam satu baris memecahnya

Andy
sumber
-1

Anda dapat menggunakan cpp, tetapi menghasilkan beberapa baris kosong di mana ia menggabungkan output, dan beberapa pengantar yang saya hapus dengan sed - mungkin itu dapat dilakukan dengan cpp-flags dan opsi juga:

echo 'foo bar \
bash \
baz
dude \
happy' | cpp | sed 's/# 1 .*//;/^$/d'
foo bar bash baz
dude happy
Pengguna tidak diketahui
sumber
Apakah Anda yakin cpp adalah solusi? Dalam contoh Anda, echostring dengan tanda kutip ganda sudah menampilkan teks yang diluruskan, jadi cpptidak ada gunanya. (Ini juga berlaku untuk sedkode Anda .) Jika Anda menempatkan string dalam tanda kutip tunggal, cppcukup hapus garis miring terbalik tetapi tidak menyatukan garis. (Rangkaian dengan cppakan bekerja jika tidak ada ruang sebelum backslash, tetapi kemudian kata-kata yang terpisah akan bergabung tanpa pemisah.)
manatwork
@manatwork: Outsch! :) Saya heran, bahwa perintah sed bekerja, tapi tentu saja, itu bukan perintah sed, tetapi bash itu sendiri mengartikan backslash-linebreak sebagai kelanjutan dari baris sebelumnya.
pengguna tidak dikenal
Menggunakan cppseperti itu masih belum menyatukan garis bagi saya. Dan penggunaan sedjelas tidak perlu. Gunakan cpp -P: “ -PMenghambat pembuatan linemarker pada output dari preprocessor.” - man cpp
manatwork
Perintah Anda tidak bekerja untuk saya: 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 --versionmengungkapkan cpp (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3- apa? Ubuntu sedang menambal cpp? Mengapa? Saya akan diharapkan untuk membaca GNU ...
pengguna tidak diketahui
Menarik. Ubuntu cppmemang menyatukan baris dan meninggalkan beberapa kekosongan. Yang lebih menarik, versi yang sama 4.4.3-4ubuntu5.1 di sini menerima -P. Namun itu hanya menghilangkan tanda garis, garis kosong tetap ada.
manatwork