Estou tentando adicionar texto ao final de uma linha nas primeiras x vezes que isso ocorre. Eu sei como fazer isso globalmente e para a ocorrência n. Não consigo descobrir como fazer isso nas primeiras enésimas ocorrências. Um exemplo seria um arquivo text.txt que contém:
This is a test
junk
This is a test
More junk
This is a test
This is a test
This is a test
E quero adicionar um '.' no final das três primeiras vezes que ocorre "Isto é um teste". A saída que estou tentando obter é:
This is a test.
junk
This is a test.
More junk
This is a test.
This is a test
This is a test
Responder1
This.*test
é o regex correto. O asterisco significa "0 ou mais vezes o caractere anterior", portanto This*test
não corresponderia a nenhuma de suas linhas.
Agora, Sed é ruim em aritmética. Para algo elegante sugiro Awk:
awk '/This.*test/{c++};{print $0 (c<4 ? "." : "")}' file
Acho que basta dizer que c
, como qualquer variável não definida no Awk, é tratada como zero, mas deixe-me saber se precisar de mais esclarecimentos.
Responder2
Outra variante que evita fazer a correspondência de regexp após todas as 3 ocorrências já terem sido encontradas:
awk -v n=3 'n && /This is a test/ {n--; $0 = $0 "."}; {print}'
Especificamente sed
, você poderia fazer algo como:
sed '
1 {
x
s/^/.../
x
}
/This is a test/ {
s/$/./
x
s/.//
/./ {
x
b
}
g
:1
$! {
n
b 1
}
}'
Onde rastreamos o número de .
s para anexar como número correspondente de .
s no espaço de espera.
Nem é preciso dizer que sed
é muito menos apropriado para esse tipo de tarefa. Se a razão para querer sed
for a -i
extensão para edição no local encontrada em algumas implementações (emprestada de perl
), observe que a implementação GNU de awk
também pode fazer isso com -i /usr/share/awk/inplace.awk
¹, ou você pode usar a versão real:
perl -lpi -e '
if ($n < 3 && /This is a test/) {
$n++;
$_ .= ".";
}' your-file
Se você quisesse adicionar um .
após cada ocorrência de This is a test
em vez de todas as linhas que contêm pelo menos uma ocorrência de This is a test
, perl
também seria a melhor escolha:
perl -pi -e 's{This is a test\K}{$n++ < 3 ? "." : ""}ge' your-file
¹não use-i inplace
as gawk
tenta carregar primeiro a inplace
extensão (como inplace
ou inplace.awk
) do diretório de trabalho atual, onde alguém poderia ter plantado malware. O caminho da inplace
extensão fornecida gawk
pode variar de acordo com o sistema, consulte a saída degawk 'BEGIN{print ENVIRON["AWKPATH"]}'
Responder3
Com perl
poderíamos fazer como mostrado
perl -lpe '
$_ = $k == 3 ? next : s/This is a test(?{$k++}).*\K/./r;
' file
Os elefantes também podem dançar, embora em passos simples. Usando GNU sed
a escrita em seu modo regex estendido, -E
podemos armazenar a contagem como o número de novas linhas na espera.
K=3
sed -Ee '
/This is a test/!b
G
/(.*\n){'"$K"'}.*\n/!{
s/\n+/./p;z;H;d
}
s/\n+//
:a;n;ba
' file