Como posso substituir o texto na última linha não vazia?

3

Eu quero encontrar a última linha de texto em um arquivo e excluir a vírgula no final dela. Eu perguntei sobre isso , mas, depois que recebi uma resposta, percebi que minha pergunta não era específica o suficiente.

Este comando sed irá para a última linha de um arquivo e tomará providências. No meu caso, quero remover a vírgula final:

sed -i '$ s/",/"/g' file.txt

Então isso:

blah blah blah,
blah blah blah,
blah blah blah,

... torna-se isso:

blah blah blah,
blah blah blah,
blah blah blah

No entanto, isso não funcionará se houver linhas em branco após a última linha de texto no arquivo.

Eu tenho procurado maneiras de obter a última linha de texto, mas não encontrei nada que eu possa entender e aplicar. Também procurei maneiras de remover todas as linhas em branco finais e encontrei este comando:

sed -e :a -e '/^\n*$/{$d;N;ba' -e '}' *.txt

Mas isso não funciona para mim (parece apenas produzir o conteúdo dos meus arquivos na linha de comando). De qualquer forma, é deselegante. Eu prefiro não excluir as linhas em branco à direita, seria muito melhor apenas identificar a última linha com texto e agir sobre isso.

Como faço para remover a vírgula da última linha de texto em vários arquivos em um diretório?

    
por Questioner 21.02.2013 / 05:10

4 respostas

2

Resposta

perl -0777 -p -i -e 's/,(\n*)\Z//m' *.txt

removerá o último ',' em todos os arquivos que terminem em .txt , se o ',' for seguido apenas por 0 ou mais caracteres de nova linha e depois pelo final do arquivo.

Do seu exemplo:

reedm@www:~/tmp $ cat > test.txt
blah blah blah,
blah blah blah,
blah blah blah,


reedm@www:~/tmp $ perl -0777 -p -i -e 's/,(\n*)\Z//m' *.txt
reedm@www:~/tmp $ cat test.txt
blah blah blah,
blah blah blah,
blah blah blah


reedm@www:~/tmp $ 

Wat?

Perl é um animal esotérico na melhor das hipóteses, e perl one-liners pode ser particularmente enigmático.

O sinalizador -e nos permite passar um programa perl na linha de comando. Neste caso, o 's / regex / substituir / sinalizadores' é o programa.

O -p flag faz com que o perl aplique seu programa fornecido em um loop sobre cada "linha" (veja -0 ) para cada nome de arquivo fornecido.

O -i flag faz com que o perl substitua o arquivo pela saída do programa, em vez de imprimir a saída como saída padrão.

O sinalizador -0 altera o que o delimitador perl usa para dividir um arquivo em "linhas". 0777 é um valor especial, usado por convenção para fazer o perl ler todo o arquivo em uma única "linha".

A expressão regular é um pouco complicada pelo uso de alguns truques específicos de perl:

  • Primeiro, o sinalizador m no final faz com que o regex funcione várias linhas.
  • , é simples e corresponde a uma única vírgula literal.
  • (\n*) corresponde a 0 ou mais linhas novas em uma linha e as armazena como subpadrão (os caracteres ( e ) denotam um subpadrão). Como isso é o primeiro subpadrão, podemos usar na seção de substituição para significar "qualquer que seja este subpadrão combinado".
  • \Z é uma extensão específica do perl e corresponde ao final da string sendo trabalhado com - neste caso, esse é o arquivo inteiro.
  • Na peça de substituição, usamos para substituir a correspondência apenas a série de novas linhas, removendo a vírgula.

Para informações de homem sobre expressões regulares perl e sinalizadores de linha de comando perl, confira as man pages para perlre e perlrun , respectivamente.

    
por 22.02.2013 / 02:16
3

Em arquivos grandes, use a resposta do Guru, é mais rápido. Em arquivos pequenos (< 25 linhas), no entanto, achei que isso seja um pouco mais rápido (supondo que você tenha o GNU tac):

tac file | awk '!/^[[:blank:]]*$/{i++;if(i==1){sub(",$","")}}1' | tac
    
por 21.02.2013 / 07:01
3

Se uma solução Perl for aprovada para você:

 perl -00pe 's/(.*),/$1/s' file

Para salvar as alterações no próprio arquivo:

perl -i -00pe 's/(.*),/$1/s' file

Para aplicar isso em vários arquivos:

perl -i -00pe 's/(.*),/$1/s' *.txt
    
por 21.02.2013 / 05:57
1

sed pode ser colocado na pesquisa de várias linhas & substitua o modo para que ele possa manipular todo o conteúdo do arquivo como uma única linha no "espaço padrão".

(Nota: O FreeBSD sed requer a seqüência de opções -n -i -e para a edição correta do arquivo no local).

# delete the last , in the last non-empty line of a file
# cf. http://austinmatzko.com/2008/04/26/sed-multi-line-search-and-replace/

cat -n testfile

sed -n -i -e '
# if the first line copy the pattern to the hold buffer
1h
# if not the first line then append the pattern to the hold buffer
1!H
# if the last line then ...
$ {
# copy from the hold to the pattern buffer
g
# remove last , in last non-empty line of file 
#s/,\([^,[:cntrl:]]*\n*\)$//
s/,\([^,]*\)$//
p
}' testfile
    
por 26.12.2013 / 18:37