Dada a tag vi
nesta questão, e o fato de que eu descobri que a edição automatizada de arquivos com Comandos ex
compatíveis com POSIX recebem pouca atenção neste site em comparação com a infinidade de conselhos sobre sed
, awk
, grep
e até Perl, aqui está uma ex
compatível com POSIX comando que irá realizar a filtragem desejada:
ex -sc 'g/.*\(on line\)/s/// | .w!>>output
q!' input
Observe a nova linha embutida no comando - isso é necessário para a portabilidade completa do POSIX, já que não há outra maneira definida de terminar o comando g
lobal; no entanto, as implementações mais permitem vários comandos -c
, caso em que o seguinte liner funcionaria da mesma forma:
ex -sc 'g/.*\(on line\)/s/// | .w!>>output' -c 'q!' input
Há um pouco de magia regex e muito ex
-command magic contido neste comando, e como ex
não parece ser amplamente conhecido, explicarei cada parte:
-s
inicia ex
no modo silencioso, "em preparação para processamento em lote", portanto, nada é enviado para o terminal.
-c
significa "Execute o seguinte comando quando o arquivo for aberto." ( input
é o nome do arquivo a ser aberto).
O comando ex
em si é realmente dois comandos:
g/.*\(on line\)/s/// | .w!>>output
q!
g
é o comando "global" e significa "Execute os seguintes comandos (o resto da linha) em todas as linhas do arquivo que correspondem à regex especificada."
O regex dado é .*\(on line\)
, que significa 'Qualquer caractere qualquer número de vezes, incluindo 0, seguido de' on line ''. Os parênteses são usados para capturar "on line" para backreferencing mais tarde.
Na verdade, o comando g
poderia ser igual a g/on line/
e funcionaria da mesma forma. No entanto, o comando s
ubstitute que escrevi usa nada para sua regex - s//
- que significa "reutilizar a última expressão regular usada". Em seguida, o comando s
usa
para o texto de substituição, o que significa "on line", neste caso.
O símbolo de pipe |
em um comando ex
não significa um pipe como no shell. Em vez disso, é normalmente usado para delimitar comandos ex
separados, cada um para ser executado sequencialmente, mas de forma independente. No entanto, o comando g
lobal é uma exceção a isso: em um comando global, a barra vertical separa os comandos que estão dentro do comando global - isto é, esses comandos são executados somente as linhas que correspondem ao regex especificado no comando global.
O comando após a barra vertical é, neste caso, um comando w
rite. É precedido por um ponto .
especificando "linha atual"; sem esse especificador de endereço, o comando write gravará o arquivo inteiro , independentemente da linha atual. (Como estamos usando o comando write dentro de um comando global, se omitirmos o ponto, o comando write gravaria todo o arquivo depois que cada linha correspondente tiver o comando de substituição executado nele!)
O >>
significa "Se o arquivo já existir, anexe-o em vez de dar um erro." Como estamos gravando no arquivo várias vezes, isso é necessário, caso contrário, acabaríamos com a linha last que foi gravada no arquivo de saída. O !
anterior ao >>
significa "Se o arquivo não já existir, crie o arquivo e grave nele em vez de gerar um erro." (Sem o !
não é especificado no POSIX se isso aconteceria ou não.) E é claro que output
é o nome do arquivo para gravar.
Por fim, é claro, q!
significa "sair sem salvar as alterações no arquivo atual". Fizemos substituições em muitas linhas do arquivo input
, mas não queremos salvar essas alterações, então usamos q!
.
Existem algumas outras abordagens que são equivalentes, por exemplo, as seguintes:
ex -sc '%s/.*\(on line\)//e | v//d
w output | q!' input
Mas isso usa o e
flag para o comando substitute, que não está no POSIX. (Se esse sinalizador for omitido, o processamento em lote será interrompido na ocasião em que o regex .*\(on line\)
não for encontrado em nenhum lugar do arquivo.)
Claro, onde ex
realmente brilha está na edição de arquivos no local . Mas certamente pode ser usado para filtrar um arquivo para outro arquivo, conforme ilustrado acima.