Deve haver uma maneira melhor de substituir somente as novas linhas únicas?

26

Eu tenho o hábito de escrever uma linha por frase, porque normalmente eu compilo coisas para o LaTex, ou escrevo em outro formato onde as quebras de linha são ignoradas. Eu uso uma linha em branco para indicar o início de um novo parágrafo.

Agora, eu tenho um arquivo escrito neste estilo que gostaria de enviar como texto simples. Eu quero remover todas as quebras de linha únicas, mas deixar as quebras de linha duplas intactas. Foi o que eu fiz:

sed 's/$^/NEWLINE/' file.txt | awk '{printf "%s ",$0}' | sed 's/NEWLINE/\n\n/g' > linebreakfile.txt

Isso substitui as linhas vazias por algum texto que estou confiante de que não aparece no arquivo: NEWLINE e, em seguida, ele se livra de todas as quebras de linha com o awk (descobri esse truque em algum site) e então substitui o NEWLINE s com as duas quebras de linha necessárias.

Esta parece ser uma maneira longa de fazer uma coisa bem simples. Existe uma maneira mais simples? Além disso, se houvesse uma maneira de substituir vários espaços (que às vezes entram por algum motivo) com espaços simples, isso também seria bom.

Eu uso emacs, então se há algum truque específico do emacs que é bom, mas eu prefiro ver um puro sed ou uma versão awk pura.

    
por Seamus 04.02.2011 / 19:47

11 respostas

18

Você pode usar o awk assim:

$ awk ' /^$/ { print; } /./ { printf("%s ", $0); } ' test

Ou se você precisar de uma nova linha no final:

$ awk ' /^$/ { print; } /./ { printf("%s ", $0); } END { print ""; } ' test

Ou se você quiser separar os parágrafos por uma nova linha:

$ awk ' /^$/ { print "\n"; } /./ { printf("%s ", $0); } END { print ""; } ' test

Esses comandos do awk fazem uso de ações que são protegidas por padrões:

/regex/

ou

END

Uma ação a seguir só é executada se o padrão corresponder à linha atual.

E os caracteres ^$. têm um significado especial nas expressões regulares, onde ^ corresponde ao início da linha, $ o final e . um caractere arbitrário.

    
por 04.02.2011 / 20:25
9

Use o modo de parágrafo do Awk ou do Perl para processar um arquivo parágrafo por parágrafo, onde os parágrafos são separados por linhas em branco.

awk -vRS= '
  NR!=1 {print ""}      # print blank line before every record but the first
  {                     # do this for every record (i.e. paragraph):
    gsub(" *\n *"," "); # replace newlines by spaces, compressing spaces
    sub(" *$","");      # remove spaces at the end of the paragraph
    print
  }
'
perl -000 -pe '             # for every paragraph:
  print "\n" unless $.==1;  # print a blank line, except before the first paragraph
  s/ *\n *(?!$)/ /g;        # replace newlines by spaces, compressing spaces, but not at the end of the paragraph
  s/ *\n+\z/\n/             # normalize the last line end of the paragraph
'

É claro que, uma vez que isso não analisa o (La) TeX, ele irá mutilar horrivelmente comentários, ambientes textuais e outras sintaxes especiais. Você pode querer olhar em DeTeX ou outro (La) TeX-para-conversores de texto.

    
por 05.02.2011 / 00:21
8

Solução Sed

$ sed -e ':a;N;$!ba;s/\(.\)\n/ /g' -e 's/\n/\n\n/' test.text

Observe que, nesta solução, :a está criando um rótulo e não está usando o comando a .

Substituindo vários espaços

Use tr : $ tr -s ' ' <test.text

    
por 04.02.2011 / 21:00
8

Se entendi corretamente, uma linha vazia implica duas novas linhas consecutivas, \n\n .

Nesse caso, uma solução possível seria eliminar todas as ocorrências singulares de novas linhas.

Em Perl, uma afirmação antecipada é uma maneira de conseguir isso:

$ perl -0777 -i -pe 's/\n(?=[^\n])//g' test
  • O -0777 sinaliza efetivamente o arquivo inteiro em uma única string
  • -p diz ao perl para imprimir a string em que está trabalhando por padrão
  • -i especifica a edição no local
  • A correspondência global garante que todas as ocorrências de nova linha sejam tratadas
por 04.02.2011 / 21:02
6

(revivendo uma pergunta antiga)

Isto parece ser exatamente o que fmt e par são para - reformatação de parágrafo. Como você (e também como muitos programas), eles definem limites de parágrafo como uma (ou mais) linhas em branco. Tente passar seu texto por um desses.

fmt é um utilitário unix padrão e pode ser encontrado no GNU Coreutils.

par é um fmt muito aprimorado escrito por Adam M. Costello, que pode ser encontrado no link ( também foi empacotado para várias distribuições, incluindo debian - eu empacotei para debian em janeiro de 1996, embora haja um novo mantenedor para o pkg agora.).

    
por 28.08.2012 / 03:23
6
sed -e'/./{H;$!d;}' -e'x;s/\n//g'

sed acrescentará qualquer linha ao espaço H old que contenha pelo menos um único caractere. Ele imediatamente depois de d eletera todos aqueles com exceção talvez do último. As únicas linhas que podem permanecer são espaços em branco, e é nessas linhas quando sed e x altera os espaços de retenção e padrão e exclui todos os caracteres \n ewline acumulados.

Se você quiser que as linhas que contêm apenas ou sejam consideradas em branco, substitua o endereço /./ acima por /[^[:blank:]]/ . Para também espremer os espaços:

 sed -e'/./{H;$!d;}'    \
     -e'x;s/\n//g'      \
     -e's/\([[:blank:]]\)*//g'
    
por 04.08.2014 / 22:28
5

Depois de ver exemplos compactos de perl e awk de Gilles, eu relutei em postar isso, mas eu já tinha passado pelo exercício, e é um roteiro funcional, que é razoavelmente documentado; este ponto por si só pode ser de interesse para alguns .. (sed com comentários! :)

Este script considera as linhas em branco como espaços em branco, mesmo que contenham espaços em branco.
Vários espaços no texto são condensados em um único espaço.
O espaço em branco à direita é removido das linhas de texto. Linhas em branco consecutivas são reduzidas a uma única linha. O script deixa as linhas em branco superior e inferior intactas.

Para qualquer coisa mais do que os scripts mais triviais, o sed pode ser escrito muito mais facilmente de forma estruturada, como um arquivo de script separado. Aqui está um exemplo.

usando sintaxe de regex estendida
chame: $ sed -rf script texto-arquivo

  :first-empty-line
  #================
  /^[[:space:]]*$/ { # if pattern-space is empty...
      $q  # last line # flush-quit 
      n   # pattern-flush=nextline-continue

      :subsequent-empty-line
      #=====================
      /^[[:space:]]*$/ { # if pattern-space is empty...
          $d        # last line # pattern-delete-cycle
          N         # pattern+=nl+nextline
          s/.*\n//  # scrap the leading 'blank' line
          t subsequent-empty-line # branch-on-substitute
      }
  }

  :text-line
  #=========
  $q                       # last line # flush-quit 
  s/^(.*)[[:space:]]*//  # trim trailing whitespace
  s/ +/ /g                 # condense mulltiple spaces
  N                        # pattern+=nl+nextline
  /^.*\n[[:space:]]*$/ { # if newly-read line is blank 
      P          # pattern-first-line-print
      s/^.*\n//  # remove the leading 'text' line
      t first-empty-line   # branch-on-substitute
  }
  # read line is text
  s/\n/ /      # replace \n with a space
  t text-line  # branch-on-substitute

Nota: flush , nos comentários, significa: enviar o espaço de padrão para o processamento de stdout interno do sed. Isso não significa uma impressão definitiva para stdout. A saída depende da opção -n do sed. por exemplo. o comando q significa flush e quit ... Compare estes dois trechos: echo x |sed -e q imprime x, echo x |sed -ne q não imprime nada, enquanto usar o comando p imprimirá 'x' duas vezes ou uma vez, dependendo da opção -n .

    
por 13.08.2011 / 00:55
4

Aqui está outra solução de sed que concatena todas as linhas em "hold space" de sed para que possamos obter uma longa string que finalmente seja copiada para o "espaço padrão" para correspondência de padrões.

Como novas linhas serão preservadas na longa cadeia final no "espaço padrão" de sed , linhas vazias em termos de quebras de linha duplas [^\n]\n\n[^\n] podem ser correspondidas e modificadas para [^\n]\n[^\n] .

Para obter mais informações, consulte, por exemplo, sed e pesquisa em várias linhas e substitua .

text='
line 1

line 2
line 3





line 4


line     5



line 6
line 7

line 8
'

# FreeBSD sed
# first sed deletes first / last line if empty and squeezes multiple spaces
printf '%s' "$text" |
sed -e '1{/^$/d;}' -e '${/^$/d;}' -e '/[[:space:]]\{2,\}/s// /g' | 
sed -n -e '1h;1!H;${;g;/\([^[:cntrl:]]\)\n\n\([^[:cntrl:]]\)/s//\
/g;p;}' |
nl -b a


# GNU sed
# alternative using ...;x;... instead of ...;g;...
# cf. man sed | less -p '\]x'
printf '%s' "$text" |
gsed -e '1{/^$/d;}' -e '${/^$/d;}' -e '/[[:space:]]\{2,\}/s// /g' | 
gsed -E -n '1h;1!H;${;x;/([^\n])\n\n([^\n])/s//\
/g;p;}' | 
nl -b a


# remove all the single linebreaks but leave the double linebreaks intact
printf '%s' "$text" | 
   sed -n -e '1h;1!H;${;g;/\([^[:cntrl:]]\)\n\([^[:cntrl:]]\)/s// /g;p;}' | 
   nl -b a
    
por 12.08.2011 / 20:04
3

Isso pode ser da velha escola:

(echo ".pl 1" ; echo ".ll 80" ; echo ".ad l" ; cat your_file) | nroff

Isso produzirá o texto alinhado à esquerda ( .ad l ), com comprimento de linha de 80 ( .ll 80 ). A opção de tamanho da página ( .pl ) informa ao processador de texto para fazer o preenchimento da página para o tamanho da página de 1, portanto, nenhum preenchimento de página.

Se você quiser todos os seus parágrafos em uma única linha, você pode usar um número grande para .ll :

(echo ".pl 1" ; echo ".ll 1000000" ; echo ".ad l" ; cat your_file) | nroff

man 7 groff para mais opções de formatação.

    
por 11.08.2011 / 20:35
1

No Emacs, às vezes eu uso esse regex :

^J\([^^J]\) -> 

Significa:

replace every newline that is followed by something which is NOT a newline with only the thing, that followed the newline That way I get rid of all newlines within a paragraph but keep paragraphs (double-newlines)

    
por 09.07.2015 / 09:33
0

Acontece que com auto-fill-mode on, o emacs faz um ótimo trabalho para meus casos de uso simples com apenas M-q ...

    
por 11.08.2011 / 15:58