Quero remover uma linha de um arquivo que contém um caractere específico apenas uma vez, se ele estiver presente mais de uma vez ou não estiver presente, mantenha a linha no arquivo.
Por exemplo:
DTHGTY
FGTHDC
HYTRHD
HTCCYD
JUTDYC
Aqui, o caractere que eu quero remover é C
assim, o comando deve remover as linhas FGTHDC
e JUTDYC
porque elas têm C
exatamente uma vez.
Como posso fazer isso usando sed
ou awk
?
fonte
awk
separador de campo!awk 'BEGIN { print "FS={" FS"}","OFS={" OFS "}";} {printf "%d fields : ",NF; for (i=1;i<=NF;i++) {printf "{" $i "} ";}; print "" }'
e alimentá-lo com algumas linhas, alguns com múltiplos spces, e outros begininng com espaço (s))abordagem sed :
-i
opção permite modificação de arquivo no local/^[^C]*C[^C]*$/
- corresponde a linhas que contêmC
apenas uma vezd
- excluir linhas correspondentesfonte
Isso pode ser feito com
sed
:Código:
Resultados:
Quão?
C
via/C.*C/p
C
via/C/d
, isso inclui as linhas já impressas na etapa 1fonte
Isso remove as linhas com exatamente uma ocorrência de C.
A expressão regular
[^C]
corresponde a um caractere que não é C (ou nova linha) e o operador de repetição (também conhecido como estrela de Kleene)*
especifica zero ou mais repetições da expressão anterior.A saída padrão de
grep
(e a maioria das outras ferramentas orientadas a texto) é a saída padrão; redirecione para um novo arquivo e, talvez, mova-o sobre o arquivo original, se é isso que você deseja. O mesmo regex pode ser usado comsed -i
a edição no local:(Em algumas plataformas, especialmente * BSD, incluindo macOS, a
-i
opção requer um argumento, como-i ''
.)fonte
sed -i '/^[^C]*C[^C]*$/d' file
- parece que foi publicado antes, como você acha plágio?grep
resposta, mas obviamente ela se estende facilmente àsed -i
variante. Não encontrou sua resposta porque estava procurando porgrep
respostas anteriores .-i
comsed
e em vez de redirecionamento para um novo arquivo e substituir o original com que se ased
utilidade saiu com nenhum erro.grep -vx '[^C]*C[^C]*'
grep
porque é mais claro e robusto (em particular,sed
possui um código de saída menos informativo).A ferramenta POSIX para edições com script de um arquivo (em vez de imprimir o conteúdo modificado para o padrão) é
ex
.Claro que você pode usar
sed -i
se a sua versão do Sed suportar, apenas lembre-se de que não é portátil se você estiver escrevendo um script destinado a ser executado em diferentes tipos de sistemas.David Foerster perguntou nos comentários:
Resposta: Sim.
Para
printf
vs.echo
é uma questão de portabilidade; consulte Por que printf é melhor que eco? E também é mais fácil intercalar novas linhas entre comandos usandoprintf
.Para
printf ... | ex
vs.ex -c ...
, é uma questão de tratamento de erros. Para este comando específico, isso não importaria, mas em geral importa; por exemplo, tente colocarem um script. Contraste com o seguinte:
O primeiro travará e aguardará entrada; o segundo sairá quando o EOF for recebido pelo
ex
comando, portanto, o script continuará. Existem soluções alternativas, comos///e
, mas elas não são especificadas pelo POSIX. Eu prefiro usar o formulário portátil, que é mostrado acima.Para o
g
comando, deve haver uma nova linha no final, e eu prefiro usarprintf
para agrupar os comandos do que incorporar uma nova linha entre aspas simples.fonte
printf
e nãoecho
ou algo parecidoex -c COMMAND
?printf
vs.echo
(embora normalmente prefira apenasecho
quando o argumento é codificado), mas ainda não o useiex
extensivamente.Aqui estão algumas opções usando perl.
Como você está correspondendo apenas a um único caractere, você pode usar
tr/C//
(uma tradução, sem substituições), para retornar o número de correspondências deC
:De maneira mais geral, se você deseja corresponder uma sequência de vários caracteres ou expressão regular, pode usar o seguinte:
Isso atribui as correspondências da expressão regular
/C/g
a uma lista@m
e imprime linhas quando o comprimento dessa lista não é1
.A
-i
opção pode ser adicionada para editar "no local".fonte
fonte
sed
,t #...
normalmente desviar para o rótulo chamado#...
na maioria das outrassed
implementações.!b
GNU sed já que branch não gosta de nada, exceto um rótulo ou uma nova linha depois dele.b
,t
,:
,}
(er file
,w file
...) não pode ter um comando depois deles na mesma linha. Você também pode usar-e
opções separadas .g
modificador.Para quem desejar
awk
especificamente, eu oferecerpule a linha se corresponder ao padrão, imprima-o de outra forma. Na verdade
{print}
, você não precisa , pode usar uma//
impressão padrão, mas acho que é mais claro.Meu primeiro pensamento foi usar
egrep -v
o mesmo padrão, mas isso não responde à pergunta como foi colocada.fonte
{next}
? Basta dizerawk '/pattern/ {next} 1'
e todas as linhas não correspondentes ao padrão serão impressos. Ou, melhor,awk '!/pattern/'
imprimi-los diretamente.!/pattern/
(o que de alguma forma escorregou na minha mente), mas eu preferiria ver um auto-explicativo do//{print}
que um enigmático1
. Assuma o mínimo de competência e fluência da próxima pessoa para manter seu código, consistente em não torná-lo menos eficiente ou eficaz.