Como escrever um sed
(ou awk
, ou ambos) que reescreverá o seguinte:
echo 'v100 v201 v102 v300 v301 v500 v999 v301' | sed/awk ...
para esta saída:
v1 v2 v3 v4 v5 v6 v7 v5
ou seja, cada subsequente vx
foi reescrito para começar v1...vn
e onde o mesmo v
foi usado na sequência (ou seja v301
) o mesmo v
deve ser aplicado (como em v5
).
Nota lateral: a sequência de entrada de exemplo mostra todas as eventualidades possíveis (ou seja, duplicatas, originais fora de ordem, saltos nos números originais).
Você é o especialista em sed ou awk que pode responder isso?
Com
perl
:v\d+
corresponde av
seguido por um ou mais dígitos decimais. O\K
afterv
redefine o início da porção correspondida,K
eeps o que está à sua esquerda, ov
, para que apenas a sequência de dígitos sejas
substituída.e
sinalizador faz com que a substituição seja tratada como código que ée
avaliado para produzir a substituição. Nesse código,$&
contém a parte correspondente.A // B
é uma forma de OR que se expande paraA
seA
for definido eB
de outra forma (em contraste comA || B
que se expande paraA
seA
resolve para um valor verdadeiroB
e de outra forma).//=
é o formulário de atribuição correspondente. EntãoA //= B
é curto paraif (defined(A)) {A} else {A = B}
.Observe que a
$seen
tabela de hash é indexada nos valores de string desses números, entãov2 v02 v002
, você obteriav1 v2 v3
como2
,02
e002
são strings diferentes umas das outras. Você pode substituir$&
por0+$&
para normalizar os números (010 sendo tratado como 10, não octal 8) para obterv1 v1 v1
o exemplo acima. Ou você pode fazers{v0*\K\d+}{$seen{$&} //= ++$n}ge
para preservar os principais0
s e obterv1 v01 v001
como resultado.Para evitar a substituição do
v1
encontrado emrev1sion
, por exemplo, você pode adicionar alguns operadores regexp de palavrasb
-chave em ambos os lados da correspondência (\bv\K\d+\b
). Ou para substituir apenas palavras delimitadas por espaços em branco (para deixarv1.2
em paz, por exemplo), adicione algumas voltas negativas para ritmos não brancosS
:(?<!\S)v\K\d+(?!\S)
.Usando
awk
:Isso passa por todos os campos de cada linha de entrada e a reatribui. O valor que é reatribuído é
v
seguido pelo próximo valor do contadorn
, a menos que o valor do campo tenha sido visto antes, caso em que seu novo valor será o mesmo que o valor desse campo foi dado anteriormente.O
1
no final aciona a saída da linha modificada.Teste:
Comando alternativo
awk
que só modifica o campo se corresponder à expressão regular^v[0-9]+$
:Ou formatado em várias linhas para facilitar a leitura:
A implementação do GNU
awk
suportaRS
ser definida como uma expressão regular e registra o que correspondeu naRT
variável especial. Então, com ele, você pode fazer algo como:Observe que ele substitui todas as ocorrências de
v
seguidas por dígitos, mesmo aquelas encontradas dentro de uma palavra como inrev1.2
ourev0lution
. Como na minha abordagem perl , você pode querer adaptá-la se os números puderem ser preenchidos com zeros.Se sua entrada contiver apenas strings de "v" seguidas por dígitos, E você estiver bem com a saída separada por espaço, este script perl pode fazer o que você deseja:
A opção do perl
-n
itera sobre cada linha de entrada (semelhante a umsed -n
script) e-l
automaticamente chomps as novas linhas das extremidades das linhas de entrada e as adiciona de volta às instruções de impressão.O
while (/(v\d+)/g)
loop itera (e captura em$1
) todas as strings correspondentesv\d+
em cada linha de entrada. Se essa correspondência não foi vista antes, incremente o contador e adicione-o ao hash %seen. Entãopush
(ou seja, adicione-o ao final) de um array chamado@line
. Depois que o loop while terminar (ou seja, depois que a linha de entrada for processada), imprima o array @line com um caractere de espaço entre cada elemento.A matriz @line é redefinida para vazia para cada linha de entrada. Se você também quiser que a numeração (
$i
) e o%seen
hash sejam redefinidos para cada linha de entrada, descomente as duas linhas antes dawhile(...)
linha:GNU awk apenas: