Existem todos os tipos de razões pelas quais a leitura de um arquivo inteiro no espaço padrão pode dar errado. O problema lógico na pergunta em torno da última linha é comum. Está relacionado ao sedciclo de linhas - quando não há mais linhas e sedencontra EOF em que está terminado - ele encerra o processamento. E assim, se você estiver na última linha e instruir sedpara conseguir outra, ela vai parar ali e não fazer mais.
Dito isto, se você realmente precisar ler um arquivo inteiro no espaço padrão, provavelmente vale a pena considerar outra ferramenta. O fato é que sedé o editor de fluxo de maneira homogênea - ele é projetado para trabalhar uma linha - ou um bloco de dados lógicos - de cada vez.
Existem muitas ferramentas semelhantes que estão melhor equipadas para lidar com blocos de arquivos completos. ede ex, por exemplo, podem fazer muito do que sedpodem fazer e com sintaxe semelhante - e muito mais - mas, em vez de operar apenas em um fluxo de entrada enquanto o transformam em saída sed, eles também mantêm arquivos de backup temporários no sistema de arquivos . O trabalho deles é armazenado em buffer no disco, conforme necessário, e eles não são encerrados abruptamente no final do arquivo (e tendem a implodir com muito menos frequência sob tensão do buffer) . Além disso, eles oferecem muitas funções úteis que sednão fazem - do tipo que simplesmente não faz sentido em um contexto de fluxo - como marcas de linha, desfazer, buffers nomeados, junção e muito mais.
sedA força principal de sua capacidade é processar dados assim que os lê - de maneira rápida, eficiente e em fluxo. Quando você copia um arquivo, joga isso fora e tende a encontrar dificuldades de casos extremos, como o último problema de linha que você mencionou, excedentes de buffer e desempenho péssimo - à medida que os dados que ele analisa aumentam no tempo de processamento de um mecanismo de expressão regular ao enumerar correspondências aumenta exponencialmente .
A respeito desse último ponto, a propósito: embora eu entenda que o s/a/A/gcaso de exemplo é muito provavelmente apenas um exemplo ingênuo e provavelmente não é o script real para o qual você deseja reunir uma entrada, você pode achar que vale a pena se familiarizar com y///. Se você costuma gsubstituir globalmente um caractere por outro, ypode ser muito útil para você. É uma transformação em oposição a uma substituição e é muito mais rápida, pois não implica uma regexp. Esse último ponto também pode torná-lo útil ao tentar preservar e repetir //endereços vazios , pois não os afeta, mas pode ser afetado por eles. De qualquer forma, y/a/A/é um meio mais simples de realizar o mesmo - e os swaps são possíveis, assim como:y/aA/Aa/ que trocaria todas as maiúsculas / minúsculas como em uma linha entre si.
Você também deve observar que o comportamento que você descreve não é realmente o que deveria acontecer de qualquer maneira.
Dos GNUs info sedna seção BUGS RELATADOS COMUNS :
A POSIXLY_CORRECTvariável de ambiente é mencionada porque o POSIX especifica que, se sedencontrar EOF ao tentar um, Nele deve sair sem saída, mas a versão GNU rompe intencionalmente com o padrão nesse caso. Observe também que, mesmo que o comportamento seja justificado acima, pressupõe-se que o caso de erro seja de edição de fluxo - não colocando um arquivo inteiro na memória.
O padrão define No comportamento da seguinte forma:
N
Anexe a próxima linha de entrada, menos sua linha de \new final , ao espaço do padrão, usando uma \nlinha de ew incorporada para separar o material anexado do material original. Observe que o número da linha atual é alterado.
Se nenhuma próxima linha de entrada estiver disponível, o Nverbo de comando deverá se ramificar no final do script e sair sem iniciar um novo ciclo ou copiar o espaço do padrão para a saída padrão.
Nessa nota, existem alguns outros GNU-ismos demonstrados na pergunta - particularmente o uso do :rótulo, brancho e {colchetes de contexto de função }. Como regra geral, qualquer sedcomando que aceite um parâmetro arbitrário delimita em uma linha de \new no script. Então os comandos ...
:arbitrary_label_name; ...
b to_arbitrary_label_name; ...
//{ do arbitrary list of commands } ...
... é provável que todos tenham um desempenho irregular, dependendo da sedimplementação que os lê. Portably eles devem ser escritos:
...;:arbitrary_label_name
...;b to_arbitrary_label_name
//{ do arbitrary list of commands
}
O mesmo vale para r, w, t, a, i, e c (e, possivelmente, um pouco mais que eu estou esquecendo no momento) . Em quase todos os casos, eles também podem ser escritos:
sed -e :arbitrary_label_name -e b\ to_arbitary_label_name -e \
"//{ do arbitrary list of commands" -e \}
... onde a nova -einstrução xecution representa o \ndelimitador de linha de ew. Portanto, onde o infotexto GNU sugere que uma implementação tradicional sedforçaria você a fazer :
/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }
... deveria ser ...
/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N
}
... é claro, isso também não é verdade. Escrever o script dessa maneira é um pouco bobo. Existem meios muito mais simples de fazer o mesmo, como:
printf %s\\n foo . . . . . . |
sed -ne 'H;/foo/h;x;//s/\n/&/3p;tnd
//!g;x;$!d;:nd' -e 'l;$a\' \
-e 'this is the last line'
... que imprime:
foo
.
.
.
foo\n.\n.\n.$
.$
this is the last line
... porque o tcomando est - como a maioria dos sedcomandos - depende do ciclo da linha para atualizar seu registro de retorno e aqui o ciclo da linha é permitido para executar a maior parte do trabalho. Essa é outra desvantagem que você faz quando inverte um arquivo - o ciclo da linha não é atualizado novamente e muitos testes se comportam de maneira anormal.
O comando acima não corre o risco de exceder a entrada, porque apenas faz alguns testes simples para verificar o que lê enquanto lê. Com o Hantigo, todas as linhas são anexadas ao espaço de espera, mas se uma linha corresponder /foo/, substituirá o hespaço antigo. Os buffers são os próximos e xalterados e uma s///substituição condicional é tentada se o conteúdo do buffer corresponder ao //último padrão endereçado. Em outras palavras, //s/\n/&/3ptenta substituir a terceira nova linha no espaço de espera e imprimir os resultados se o espaço de espera corresponder no momento /foo/. Se isso for tbem-sucedido, o script se ramifica para o rótulo not delete - o que faz um ok le envolve o script.
No /foo/entanto, se uma e a terceira nova linha não puderem ser combinadas no espaço de espera, //!gelas substituirão o buffer se /foo/não forem correspondidas ou, se forem correspondentes, substituirão o buffer se uma linha de \new não corresponder (substituindo assim /foo/por próprio) . Esse pequeno teste sutil evita que o buffer seja preenchido desnecessariamente por longos períodos sem /foo/e garante que o processo permaneça rápido porque a entrada não se acumula. Em um caso de não /foo/ou //s/\n/&/3pfalha, os buffers são novamente trocados e todas as linhas, exceto a última, são excluídas.
Essa última - a última linha $!d- é uma demonstração simples de como um sedscript de cima para baixo pode ser feito para lidar com vários casos facilmente. Quando o seu método geral é remover os casos indesejados, começando pelos mais gerais e trabalhando pelos mais específicos, os casos extremos podem ser mais facilmente tratados, porque eles simplesmente podem passar até o final do script com seus outros dados desejados e quando tudo isso envolve apenas os dados que você deseja. Porém, ter que buscar esses casos extremos de um loop fechado pode ser muito mais difícil.
E aqui está a última coisa que tenho a dizer: se você realmente precisa extrair um arquivo inteiro, pode trabalhar um pouco menos, confiando no ciclo da linha para fazer isso por você. Normalmente você usaria Next e next para lookahead - porque eles avançam à frente do ciclo da linha. Em vez de implementar redundantemente um loop fechado dentro de um loop - como o sedciclo da linha é apenas um loop de leitura simples - se seu objetivo é apenas coletar informações indiscriminadamente, provavelmente é mais fácil:
sed 'H;1h;$!d;x;...'
... que reunirá todo o arquivo ou será estourado.
uma observação lateral Ne comportamento de última linha ...
Embora eu não tenha as ferramentas disponíveis para testar, considere que Nao ler e editar no local se comporta de maneira diferente se o arquivo editado for o arquivo de script da próxima leitura.