Como posso transformar a saída feia em informação bonita e útil?

2

Como posso transformar essa saída feia em dados bonitos e úteis?

A saída:

/* ---------- TA#box#AbC_p ---------- */

insert_job: TA#box#AbC_p    job_type: a
#owner: bob
permission: gx
date_conditions: 1
days_of_week: su
start_times: "16:15"
run_window: "16:15-17:30"
description: "Job AbC that runs at 4:15PM on Sundays, and should end before 5:30PM"

    /* ---------- TA#cmd#EfGJob_p ---------- */

    insert_job: TA#cmd#EfGJob_p    job_type: b
    box_name: TA#box#AbC_p
    command: /path/to/shell/script.sh
    machine: vm_machine1
    #owner: alex
    permission: gx
    date_conditions: 2
    run_window: "16:20-16:30"
    description: "job EfG that runs within box AbC"
    term_run_time: 60
    std_out: /path/to/log.log
    std_err: /path/to/err.log
    alarm_if_fail: 1
    profile: /path/to/profile

e assim por diante, por um longo tempo. # cmd # jobs estão algumas vezes abaixo de # box #. Se eles estiverem sob um # box #, a seção # cmd # será recuada.

Meu resultado ideal seria algo como:

"Job Name", "Time", "Schedule", "Machine", "Description", "Command"
"TA#box#AbC_p", "16:15", "su", "", "Job AbC that runs at 4:15PM on Sundays, and should end before 5:30PM", ""
"TA#cmd#EfGJob_p", "16:15", "su", "vm_machine1", "job EfG that runs within box AbC", "/path/to/shell/script.sh"

Estou tentando o awk, o perl e o grep, mas estou com problemas para manter todas as informações de uma "seção" juntas antes de imprimir a linha do CSV.

    
por Joe A 18.05.2012 / 23:41

3 respostas

1

Eu usaria o Perl para isso, ou pelo menos o awk.

perl -ne '
    BEGIN {
        print "\"Job Name\", \"Time\", \"Schedule\", \"Machine\", \"Description\", \"Command\", \"\n";
    }
    chomp; s/^\s+//; s/\s+$//;
    if (($_ eq "" || eof) && exists $fields{insert_job}) {
        print "\"", join("\", \"", @fields{qw(insert_job start_times days_of_week machine description command)}), "\"\n";
        delete @fields{qw(insert_job)};
    }
    if (/^([^ :]+): *(.*)/) {$fields{$1} = $2}
'

Explicações:

  • O bloco BEGIN é executado uma vez no início do script, o restante é executado para cada linha de entrada.
  • A linha que começa com chomp retira o espaço em branco inicial e final.
  • A primeira linha if é acionada em linhas vazias (separadores de parágrafo), se o campo insert_job estiver presente.
  • A linha delete remove o campo insert_job . Adicione outros nomes de campo que você não deseja passar de um parágrafo para o outro.
  • A última linha if armazena campos.
por 19.05.2012 / 15:11
4

Um pouco de terrível sed oneliner:

sed -n  \
# we divide out incoming text to small parts, 
# each one as you mentioned from /---.*box.*/ to /profile/
'/---.*box.*/,/profile/{
     # inside of each part we do following things:
     # if string matches our pattern we extract 
     # the value and give it some identifier (which you
     # can see is "ij", "st" and so on)
     # and we copy that value with identifier to hold buffer,
     # but we don't replace the content of hold buffer
     # we just append (capital H) new var to it
     /insert_job/{s/[^:]*: /ij"/;s/ .*/",/;H};
     /start_times/{s/[^:]*: /st/;s/$/,/;H};
     /days_of_week/{s/[^:]*: /dw"/;s/$/",/;H};
     /machine/{s/[^:]*: /ma"/;s/$/",/;H};
     /description/{s/[^:]*: /de/;s/$/,/;H};
     /command/{s/[^:]*: /co"/;s/$/",/;H};
     # when line matches next pattern (profile)
     # we think that it is the end of our part,
     # therefore we delete the whole line (s/.*//;)
     # and exchange the pattern and hold buffers (x;)
     # so now in pattern buffer we have several strings with all needed variables
     # but all of them are in pattern space, therefore we can remove
     # all newlines symbols (s/\n//g;). so it is just one string 
     # with a list of variables
     # and we just need to move to the order we want,
     # so in this section we do it with several s commands.
     # after that we print the result (p)
     /profile/{s/.*//;x;s/\n//g;s/ij\("[^"]*box[^"]*",\)//;
          s/,\(.*\)st\("[^"]*",\)\(.*ij"[^"]*",\)/,/;
          s/\([^,]*,[^,]*,\)\(.*\)dw\("[^"]*",\)\(.*ij"[^"]*",[^,]*,\)//;
          s/de/"",/;s/ij/""\n/;
          s/\(\n[^,]*,[^,]*,[^,]*,\)\(.*\)ma\("[^"]*",\)//;
          s/co\("[^"]*"\),\(.*\)//;s/de//;p}
     };
     # the last command just adds table caption and nothing more.
     # note: if you want to add some new commands,
     # add them before this one
     1i"Job Name", "Time", "Schedule", "Machine", "Description", "Command"'

Eu o escrevi como a ordem de campo pode variar em caixas diferentes, mas o perfil é sempre o último. Caso a ordem seja sempre a mesma, seria um pouco mais fácil.

    
por 19.05.2012 / 01:36
0

Usando a linguagem TXR:

@(bind inherit-time nil)
@(bind inherit-sched nil)
@(collect)
@  (all)
@indent/* ---------- @jobname ---------- */
@  (and)
@/ *//* ---------- @nil#@type#@nil ---------- */
@  (end)

@  (bind is-indented @(> (length indent) 0))
@  (gather :vars ((time "") (sched "") (mach "") (descr "") (cmd "")))
@/ */start_times: "@*time"
@/ */days_of_week: @sched
@/ */machine: @mach
@/ */description: "@*descr"
@/ */command: @cmd
@  (until)

@  (end)
@  (cases)
@    (bind type "box")
@    (set (inherit-time inherit-sched) (time sched))
@  (or)
@    (bind type "cmd")
@    (bind is-indented t)
@    (set (time sched) (inherit-time inherit-sched))
@  (end)
@(end)
@(output)
"Job Name", "Time", "Schedule", "Machine", "Description", "Command"
@  (repeat)
"@jobname", "@time", "@sched", "@mach", "@descr", "@cmd"
@  (end)
@(end)

Esta é uma abordagem muito ingênua. De cada registro, extraímos todos os campos nos quais estamos interessados, substituindo espaços em branco por aqueles que não estão presentes (os valores padrão no argumento :vars de @(gather) ). Nós prestamos atenção ao tipo de trabalho ( box ou cmd ) e recuo. Quando vemos uma caixa, copiamos algumas propriedades da caixa em variáveis globais; e quando vemos um cmd indentado, ele copia essas propriedades. (Assumimos cegamente que eles foram configurados por umbox anterior.)

Executar:

$ txr jobs.txr jobs
"Job Name", "Time", "Schedule", "Machine", "Description", "Command"
"TA#box#AbC_p", "16:15", "su", "", "Job AbC that runs at 4:15PM on Sundays, and should end before 5:30PM", ""
"TA#cmd#EfGJob_p", "16:15", "su", "vm_machine1", "job EfG that runs within box AbC", "/path/to/shell/script.sh"

Observe que a saída é campos entre aspas separados por vírgulas, mas nada é feito com relação à possibilidade de que os dados contenham cotações. Se aspas são de alguma forma escapadas em description: , então isso será preservado, é claro. A notação @*descr é uma correspondência gulosa e, portanto, description: "a b"c\"d" resultará em descr assumindo os caracteres a b"c\"d , que serão reproduzidos na íntegra na saída.

O interessante dessa solução é que, se não tivermos um exemplo dos dados, podemos adivinhá-los a partir da estrutura do código, já que ele expressa uma correspondência de padrão ordenada no arquivo. Podemos ver que há seções sendo coletadas que começam com uma linha /* --- ... --- */ , na qual um nome de trabalho é incorporado, e que há um campo de tipo entre duas marcas de hash no meio do nome do trabalho. Em seguida, uma linha em branco obrigatória segue após as quais as propriedades são reunidas até outra linha em branco e assim por diante.

    
por 27.10.2013 / 10:13