AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 526995
Accepted
Caleb
Caleb
Asked: 2019-06-27 02:41:30 +0800 CST2019-06-27 02:41:30 +0800 CST 2019-06-27 02:41:30 +0800 CST

Reordenar linhas e mesclar outras com base em critérios específicos

  • 772

Um ponto fraco no meu cli foo é awk. Eu provavelmente poderia resolver o seguinte com scripts elaborados, mas tenho certeza de que awké a melhor ferramenta para o trabalho e, pela minha vida, não consigo descobrir a abordagem correta.

Digamos que eu tenha um arquivo de dados como este (Ledger):

2019/05/31 (MMEX948) Gürmar
    Assets:Cash:Marina                       ₺-28,14
    Expenses:Food:Groceries:Meat              ₺28,14
    Assets:Cash:Marina                       ₺-28,14
    Expenses:Food:Groceries:Meat              ₺28,14
    Assets:Cash:Marina                        ₺-3,45
    Expenses:Food:Groceries:Basic              ₺3,45
    Assets:Cash:Marina                       ₺-15,00
    Expenses:Food:Groceries:Produce           ₺15,00

2019/06/01 (MMEX932) A101
    Assets:Cash:Caleb                     $-3.00
    Assets:Cash:Marina                    $-2.50
    Expenses:Food:Groceries:Basic          $5.50

2019/06/01 (MMEX931) Şemikler Pazar Yeri
    Assets:Cash:Marina                       ₺-24,00
    Expenses:Food:Groceries:Basic             ₺24,00
    Assets:Cash:Marina                       ₺-31,00
    Expenses:Food:Groceries:Meat              ₺31,00
    Assets:Cash:Marina                       ₺-65,00
    Expenses:Food:Groceries:Produce           ₺65,00

Cada parágrafo separado por linha em branco é uma transação , cada linha recuada é um lançamento , cada lançamento tem uma conta e um valor (separado por pelo menos 2 espaços).

Eu quero que duas coisas aconteçam com esses dados. Eu não me importo se isso acontecer no mesmo comando ou não, pode ser mais fácil fazer em uma ou duas passagens, dependendo da ferramenta ...

  1. Todos os lançamentos com valores negativos devem ser organizados após os lançamentos com valores positivos.

  2. Quaisquer lançamentos com valores negativos e contas duplicadas devem ser mesclados. Idealmente, os valores seriam somados, mas isso é muito complicado por causa dos formatos de moeda e não é necessário porque posso regenerar as linhas de valor. A remoção total do valor das postagens mescladas é suficiente, desde que não mais de uma conta única seja mesclada por passagem.

O resultado deve ficar assim:

2019/05/31 (MMEX948) Gürmar
    Expenses:Food:Groceries:Meat              ₺28,14
    Expenses:Food:Groceries:Meat              ₺28,14
    Expenses:Food:Groceries:Basic              ₺3,45
    Expenses:Food:Groceries:Produce           ₺15,00
    Assets:Cash:Marina

2019/06/01 (MMEX932) A101
    Expenses:Food:Groceries:Basic          $5.50
    Assets:Cash:Marina                    $-2.50
    Assets:Cash:Caleb

2019/06/01 (MMEX931) Şemikler Pazar Yeri
    Expenses:Food:Groceries:Basic             ₺24,00
    Expenses:Food:Groceries:Meat              ₺31,00
    Expenses:Food:Groceries:Produce           ₺65,00
    Assets:Cash:Marina

Observações que tornam isso um pouco mais complicado do que apenas uma verificação de duplicatas:

  • Na primeira transação, há duas contas diferentes que são duplicadas. Apenas um deles deve ser mesclado e limpo (seria possível mesclar os dois, mas apenas um por passagem ou não poderei corrigir os valores).
  • Na transação intermediária não há nada para mesclar, mas seria um erro limpar cegamente os valores de todas as transações negativas. Como não há mesclagem, ela não precisa ser limpa, mas pode ser se isso facilitar o processamento.

Como eu passaria por esse problema em awk? Ou se Awk não é a melhor solução, qual é? Na maioria das linguagens de script (perl, python, zsh), eu analisaria tudo, jogaria tudo em uma matriz multidimensional, classificaria com base em correspondências regex do valor e secundariamente em alfa para as contas, então iteraria sobre ele para produzi-lo, sempre elimine a última quantia e mescle apenas a última duplicata (se houver).

Observe que eu trabalhei em uma maneira de analisar e mesclar transações duplicadas no Awk outro dia:

awk 'NF { if (/^20/) { if (last != $$0) print "\n" $$0; last = $$0 } else { print $$0 } }' |

Mas uma lógica awk mais complicada está me desafiando agora.

awk text-processing
  • 2 2 respostas
  • 102 Views

2 respostas

  • Voted
  1. Best Answer
    muru
    2019-06-27T03:59:21+08:002019-06-27T03:59:21+08:00

    Este script GNU awk funciona para mim:

    #! /usr/local/bin/awk -f
    BEGIN { FS = "[[:space:]][[:space:]]+" }
    function dump() {
        for (acct in post) { # dump unmerged postings of current transaction
            if (post[acct])
                print post[acct];
        }
        if (merged) {   # dump merged posting, if any
            printf "    %s\n", merged
        }
        merged = "";    # clear variables for next round
        delete post;
        txn = "";
    }
    !NF && txn {        # blank line, end of transaction
        dump();
        print;
        next
    } 
    END { # end-of-file, print merged postings of last txn
        dump();
    }
    !txn {  # new transaction
        txn = $0;
        print;
        next
    }
    {
        acct = $2;
        amt = $3
    }
    amt ~ /-/ { # negative amounts, keep for later
        if (acct in post) { # duplicate entry
            if (!merged || merged == acct) { # only merge and clear one duplicate account
                post[acct] = "";
                merged = acct;
            }
            else  # tack on to existing record without merging
                post[acct] = post[acct] "\n" $0
        }
        else
            post[acct] = $0
        next
    }
    1
    

    Em ação:

    ~ ./foo.awk foo
    2019/05/31 (MMEX948) Gürmar
        Expenses:Food:Groceries:Meat              ₺28,14
        Expenses:Food:Groceries:Meat              ₺28,14
        Expenses:Food:Groceries:Basic              ₺3,45
        Expenses:Food:Groceries:Produce           ₺15,00
        Assets:Cash:Marina
    
    2019/06/01 (MMEX932) A101
        Expenses:Food:Groceries:Basic          $5.50
        Assets:Cash:Marina                    $-2.50
        Assets:Cash:Caleb                     $-3.00
    
    2019/06/01 (MMEX931) Şemikler Pazar Yeri
        Expenses:Food:Groceries:Basic             ₺24,00
        Expenses:Food:Groceries:Meat              ₺31,00
        Expenses:Food:Groceries:Produce           ₺65,00
        Assets:Cash:Marina
    
    • 2
  2. Ed Morton
    2019-06-27T06:33:16+08:002019-06-27T06:33:16+08:00

    Com GNU awk para gensub(), arrays de arrays e sorted_in:

    $ cat tst.awk
    BEGIN { RS=""; FS="\n"; localeDecPt="."; PROCINFO["sorted_in"]="@val_num_desc" }
    {
        delete sum
        print $1
        denom = gensub(/.*([^0-9.,-]).+$/,"\\1",1,$2)
        for (i=2; i<=NF; i++) {
            account = gensub(/[[:space:]]+[^[:space:]]+$/,"",1,$i)
            amount  = gensub(/.*[^0-9.,-](.+)$/,"\\1",1,$i)
            inputDecPt = gensub(/[0-9-]+/,"","g",amount)
            sum[account] += gensub("["inputDecPt"]",localeDecPt,"g",amount)
        }
    
        for (account in sum) {
            amount = denom gensub("["localeDecPt"]",inputDecPt,"g",sprintf("%0.2f",sum[account]))
            printf "%-*s%*s\n", 40, account, 10, amount
        }
    
        print ""
    }
    

    .

    $ awk -f tst.awk file
    2019/05/31 (MMEX948) Gürmar
        Expenses:Food:Groceries:Meat            ₺56,28
        Expenses:Food:Groceries:Produce         ₺15,00
        Expenses:Food:Groceries:Basic            ₺3,45
        Assets:Cash:Marina                     ₺-74,73
    
    2019/06/01 (MMEX932) A101
        Expenses:Food:Groceries:Basic            $5.50
        Assets:Cash:Marina                      $-2.50
        Assets:Cash:Caleb                       $-3.00
    
    2019/06/01 (MMEX931) Şemikler Pazar Yeri
        Expenses:Food:Groceries:Produce         ₺65,00
        Expenses:Food:Groceries:Meat            ₺31,00
        Expenses:Food:Groceries:Basic           ₺24,00
        Assets:Cash:Marina                    ₺-120,00
    

    Se .não for o ponto decimal em sua localidade, basta alterar localeDecPt="."para o que for. Se os valores de entrada contiverem, digamos, vírgulas como separadores de milhares, o código que postei não funcionará e você deverá fornecer uma entrada que inclua isso para testar. Codifiquei as larguras do campo de saída para 40 e 10 - você pode calcular facilmente a largura máxima de cada campo e usá-la ou usar guias como OFS e canalizar a saída para column, mas não parece nada disso ' d ser necessário.

    Para ser honesto, não entendo seus requisitos sobre o que mesclar e como identificar duplicatas (por exemplo, por que não mesclar todas as duplicatas na primeira transação e por que limpar o valor de uma conta não duplicada na segunda transação?) apenas fundiu os valores para todas as duplicatas e deixou os valores para não duplicados. Se isso não funcionar para você, por favor, esclareça os requisitos em sua pergunta.

    • 2

relate perguntas

  • Reorganize as letras e compare duas palavras

  • Subtraindo a mesma coluna entre duas linhas no awk

  • Embaralhamento de arquivo de várias linhas

  • como posso alterar o caso do caractere (de baixo para cima e vice-versa)? ao mesmo tempo [duplicado]

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve