Como manter o registro de resultados de operações anteriores no awk?

4

Eu me deparei com outro desafio que estou completamente perplexo. Eu tenho um arquivo cheio de dados no seguinte formato. Existem três linhas que precisam ficar juntas e elas têm um padrão previsível:

dn: uid=N-NAME-02, ou=data01, dc=data02, dc=data03
uidNumber: 3423
sambaSID: S-1-1-11-1111111-111111111-11111111-12342
<blank line>
dn: uid=N-NAME-03, ou=data01, dc=data02, dc=data03
uidNumber: 3245
sambaSID: S-1-1-11-1111111-111111111-11111111-32212

Eu preciso fazer alguma matemática com base na segunda string de dados no grupo de 3 e, em seguida, colocar o cálculo resultante na terceira string do grupo:

dn: uid=NAME02, ou=data01, dc=data02, dc=data03
uidNumber: (3423 + 2 * 100)
sambaSID: S-1-1-11-1111111-111111111-11111111-342500
<blank line>
dn: uid=NAME03, ou=data01, dc=data02, dc=data03
uidNumber: (3245 + 2 * 100)
sambaSID: S-1-1-11-1111111-111111111-11111111-324700

Eu percebi que poderia usar novamente o AWK, já que o AWK pode fazer as contas e corrigir o posicionamento do valor resultante. Eu tenho a parte de matemática por conta própria e armazenei em uma variável

variable1='awk -F ': ' '/uidNumber:/ { new = $2 * 2 + 1000; print new }' infile'

Eu então pude usar essa variável em outro comando no mesmo arquivo para fazer a substituição

awk -F '-' -v variable2=$variable1 '/pattern of string 3 / { print $1"-"$2"-"$3"-"$4"-"$5"-"$6"-"$7"-"variable2 }'

Devo mencionar que os dados da string 3 são uma string longa separada por traços '-'. Depois do último traço é onde o valor calculado precisa ser colocado.

Isso tudo funciona, mas tem uma grande falha ... ele só funciona se houver um registro no arquivo. Então eu realmente não entendi. Estou tentando entender a maneira mais eficiente de fazer isso. Eu preferiria ficar com bash como eu estou mais familiarizado com isso. Alguém pode me dar uma dica sobre como fazer isso?

    
por bourne 28.05.2014 / 22:03

3 respostas

3

Você pode usar a função match() em awk :

$ cat file
somedata45
somedata47
somedata67

somedata53
somedata23
somedata12

awk '
BEGIN { RS = ""; OFS = "\n"; ORS = "\n\n" }
match($2, /[0-9]+/) { value = (substr($2, RSTART, RLENGTH) + 5) * 100 }
match($3, /[0-9]+/) { $3 = substr($2, 1, RSTART - 1) value }1' file
somedata45
somedata47
somedata5200

somedata53
somedata23
somedata2800

Nós definimos o Separador de Registros para nada efetivamente permitindo o modo de parágrafo (separado por linha em branco). A segunda linha em cada parágrafo se torna nossa $2 , a terceira linha se torna $ 3, etc. Nós definimos o Output Field Separator como nova linha. Devido ao modo de parágrafo, também definimos o Output Record Separator para duas novas linhas. A saída lhe dará uma nova linha extra no final.

Usamos a função match() para identificar o início do número. Quando uma correspondência é encontrada, a função preenche duas variáveis para nós, RSTART e RLENGTH , indicando quando a correspondência começa e por quanto tempo ela é. Usamos essas variáveis para fazer nosso cálculo e armazenar o resultado na variável chamada value . Usamos a função substr para localizar os números.

Repetimos o mesmo para $3 e, desta vez, usamos a função substr para imprimir até onde nossos números começam e substituir a peça numérica por nossa variável que contém o valor calculado da linha anterior.

Consulte as Funções de string no guia do usuário para mais detalhes.

Atualização com base em dados reais:

Seus dados reais realmente tornam muito mais simples.

awk '
/^uidNumber/ { value = $NF } 
 /^sambaSID/ { 
    n = split ($NF, tmp, /-/)
    tmp[n] = ((value + 2)* 100)
    for (i=1; i<=n; i++) { nf = (nf ? nf "-" tmp[i] : tmp[i]) }
    $NF = nf
    nf = ""
}1' file
dn: uid=NAME02, ou=data01, dc=data02, dc=data03
uidNumber: 3423
sambaSID: S-1-1-11-1111111-111111111-11111111-342500

dn: uid=NAME03, ou=data01, dc=data02, dc=data03
uidNumber: 3245
sambaSID: S-1-1-11-1111111-111111111-11111111-324700

Você procura a linha com uidNumber e captura o último campo. Quando você vê uma linha com sambaSID , divide o último campo em - e modifica o último elemento para o novo valor calculado. Você então usa um for loop para montar seu último campo.

    
por 28.05.2014 / 23:09
0

A maneira que eu faria esse tipo de coisa em um script awk é mantendo alguma variável para contar as linhas. Você pode fazer isso em um único script.

/^[ \t]*$/ { lineCnt = 0; continue } # this matches blank lines and resets the count
{ lineCnt++ }
lineCnt == 2 { ... strip off your numeric ... }
lineCnt == 3 { ... do whatever you need to with the math ... }

Você poderia torná-lo um pouco mais eficiente adicionando instruções 'continue' no final dos blocos para as linhas 2 e 3, já que você sabe que está pronto.

    
por 28.05.2014 / 22:15
0

Parece que você está apenas perguntando como combinar várias expressões de ação padrão em awk , usando os valores de variáveis de um em outro - o que é muito simples, por exemplo,

awk '/pattern2/ { calculate and assign var }; /pattern3/ {do something with var}'

O ; não é estritamente necessário neste caso, mas ajuda a compreensão. Em um script awk (ao invés de um one-liner) você provavelmente separaria as expressões com novas linhas.

Por exemplo, se file for

pattern1
pattern2 3 5
pattern3

então

$ awk '/pattern2/ {var = $2+$3}; /pattern3/ {print; print var}' file
pattern3
8

Além disso, talvez você queira dar uma olhada na variável OFS ( separador de campo de saída ) do awk, que permite formatar a saída mais facilmente, por exemplo. dado

pattern1
pattern2-3-5
pattern3-4-6

então

$ awk -F- '/pattern2/ {var = $2+$3}; /pattern3/ {OFS="-"; print $1,$2,$3,var}' file
pattern3-4-6-8
    
por 28.05.2014 / 22:26

Tags