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 / 786805
Accepted
Gao
Gao
Asked: 2024-11-18 19:36:04 +0800 CST2024-11-18 19:36:04 +0800 CST 2024-11-18 19:36:04 +0800 CST

Como imprimir todos os pares chave-valor de determinadas seções em um arquivo INI?

  • 772

O arquivo INI é o profiles.ini do Firefox . Quero imprimir tudo sob , incluindo os cabeçalhos de seção, onde os 's são inteiros não negativos, com uma linha em branco separando cada seção. Há também uma chave opcional sob que é diferente das chaves sob as seções de perfis, e quero imprimi-la também, se presente. Basicamente, o script de shell era apenas e antes (para imprimir as chaves e valores opcionais , , e ), mas isso falhará se as chaves forem adicionadas ou excluídas, e a opção de grep não for portátil.[Profilen]nDefault[Install*]Defaultgrep -E 'Default=[^1]' profiles.inigrep -A4 '^\[Profile' profiles.iniNameIsRelativePathDefault-A

Aqui está minha solução hackeada, que não é idiomática nem robusta para o AWK:

/^[[]Profile[0123456789]{1,}[]]$/ {
    print
    while ((getline) > 0) {
        if ($0 ~ /^$/) { # Should really break on new sections.
            print ""
            break
        } else {
            print
        }
    }
}
/^Default=/ {
    print # Default profile path given in the Install* section.
}

Exemplo de entrada:

[Profile2]
Name=default-test
IsRelative=0
Path=/home/user/ffprofiles/f9bwn86n.default-test

[Profile1]
Name=default
IsRelative=1
Path=x64qf7nv.default
Default=1

[Profile0]
Name=default-release
IsRelative=1
Path=9hv1fbkk.default-release-3426201712696

[General]
StartWithLastProfile=1
Version=2

[Install22379532B4E49482]
Default=9hv1fbkk.default-release-3426201712696
Locked=1

Exemplo de saída:

[Profile2]
Name=default-test
IsRelative=0
Path=/home/user/ffprofiles/f9bwn86n.default-test

[Profile1]
Name=default
IsRelative=1
Path=x64qf7nv.default
Default=1

[Profile0]
Name=default-release
IsRelative=1
Path=9hv1fbkk.default-release-3426201712696

Default=9hv1fbkk.default-release-3426201712696

Como posso fazer isso de forma mais concisa e correta? A solução não precisa estar em AWK, mas acho que awk é mais adequado neste caso do que sed ou qualquer outro utilitário Unix. A solução, no entanto, deve ser portátil e compatível com POSIX. Obrigado antecipadamente.

awk
  • 6 6 respostas
  • 501 Views

6 respostas

  • Voted
  1. grawity
    2024-11-18T20:37:55+08:002024-11-18T20:37:55+08:00

    awk é capaz de manter o estado entre linhas sem a necessidade de getline(). Se você precisar de uma solução baseada em awk, o método mais fácil seria:

    cat profiles.ini | awk '
        /^\[/                 {ok=0}
        /^\[Install\]$/       {ok=1; header=$0}
        ok && /^Default=/     {print header; print}
    '
    cat profiles.ini | awk '
        /^\[/                 {ok=0}
        /^\[Profile[0-9]*\]$/ {ok=1}
        ok                    {print}
    '
    

    Como um único script awk, isso pode funcionar:

    /^\[/                 {ok=0}
    /^\[Profile[0-9]*\]$/ {ok=1}
    /^\[Install\]$/       {ok=2; header=$0}
    ok==1                 {print}
    ok==2 && /^Default=/  {print header; print}
    

    Sempre salvar o cabeçalho e fazer uma comparação de strings pode torná-lo mais claro, mas também pode ter o resultado oposto. (Eu não testei esta versão.)

    /^\[/                 {hdr=""}
    /^\[Profile[0-9]*\]$/ {hdr=$0}
    /^\[Install\]$/       {hdr=$0}
    hdr ~ /\[Profile/               {print}
    hdr ~ /\[Install/ && /Default=/ {print hdr; print}
    
    • 3
  2. Best Answer
    Ed Morton
    2024-11-18T23:16:25+08:002024-11-18T23:16:25+08:00

    Usando qualquer awk:

    $ awk '
        /^\[/ { sect = substr($0,2) }
        (sect ~ /^Profile/) || ( (sect ~ /^Install/) && /^Default=/ )
    ' profiles.ini
    [Profile2]
    Name=default-test
    IsRelative=0
    Path=/home/user/ffprofiles/f9bwn86n.default-test
    
    [Profile1]
    Name=default
    IsRelative=1
    Path=x64qf7nv.default
    Default=1
    
    [Profile0]
    Name=default-release
    IsRelative=1
    Path=9hv1fbkk.default-release-3426201712696
    
    Default=9hv1fbkk.default-release-3426201712696
    
    • 3
  3. Marcus Müller
    2024-11-18T19:59:31+08:002024-11-18T19:59:31+08:00

    O que você está descrevendo é um formato de arquivo de texto estruturado com contexto.

    awkpode (irá) ser capaz de extrair a seção específica, mas precisaria fazer muitas suposições que não são baseadas em como o formato do arquivo realmente funciona, mas em como o exemplo específico que você pensou se parece (começando com "fácil" para abordar coisas como "a capitalização importa" até coisas mais interessantes como "como lidar com duas seções com o mesmo nome e chaves sobrepostas?").

    Então, simplesmente não use awk, sedou qualquer outra abordagem de parser livre de contexto para analisar algo como arquivos ini. Use um parser com conhecimento do formato.

    A propósito, o formato aqui é TOML .

    Você usa posix , o que indica que você pode usar um compilador C99 (que é uma ferramenta tão POSIX quanto awk!). Então, em vez de um awkanalisador TOML baseado em , vá para uma biblioteca TOML estabelecida e bem funcional. toml-cé uma biblioteca que você pode simplesmente soltar como arquivo de cabeçalho ao lado do seu arquivo .c. O examples/diretório tem dois exemplos que você pode adaptar diretamente ao seu caso de uso; basta substituir toml_parse(char*,…)por toml_parse_file(FILE*,…), e abrir o arquivo que você recebeu como argv[1]; fácil.

    Não entregaremos uma solução C99 completa só porque você acha que os utilitários POSIX são o caminho para a portabilidade – infelizmente, não é; diferentes implementações do mesmo utilitário POSIX são normalmente mais incompatíveis entre plataformas do que, digamos, interpretadores Python, e cada plataforma para a qual você obtém um awkpré-instalado muito provavelmente também tem um python.

    Falando em Python, que vem com um parser toml incluso, aqui está seu script de 9 linhas, incluindo ajuda de uso, para imprimir todas as chaves/valores de uma seção dada. Como ele dará erro com um valor de retorno diferente de zero quando você der a ele um nome de seção que não existe na sua entrada, um loop de shell simples pode ser usado para manipular Profile0, Profile1… até que ProfileN+1não exista mais. Mais elegante seria fazer isso no próprio Python, mas isso é deixado para o leitor como exemplo, já que seria muito específico, enquanto esta ferramenta é mais útil em geral:

    #!/usr/bin/env python3
    import tomllib
    from sys import argv, stdin, stderr, exit
    
    if len(argv != 2):
      stderr.print(f"USAGE: {argv[0]} SECTION-NAME < inputfile.ini\n")
      stderr.print(f"Prints all key/value pairs from a section in a INI\n")
      stderr.print(f"(or generally, TOML) file, separated by a sheep.\n")
      exit(127)
    
    ini = tomllib.load(stdin)
    section = ini[argv[1]]
    for key, value in section.items():
      print(f"{key} 🐑 {value}")
    
    • 2
  4. userene
    2024-11-19T02:36:21+08:002024-11-19T02:36:21+08:00

    Desde que você esteja feliz em extrair um bloco de chaves por vez, você pode se safar com um único comando awk. O fim de cada bloco é convenientemente outra [chave], ou o fim do arquivo.

    Aqui está o comando para extrair, digamos, [Profile0]:

    $ awk '/^\[Profile0/{p=1; print; next} /^\[/{p=0}; p>0{print}' ini.txt 
    [Profile0]
    Name=default-release
    IsRelative=1
    Path=9hv1fbkk.default-release-3426201712696
    

    ou extrair o bloco que começa com [Geral]:

    $ awk '/^\[Gen/{p=1; print; next} /^\[/{p=0}; p>0{print}' ini.txt 
    [General]
    StartWithLastProfile=1
    Version=2
    

    Usei [Gen e [Profile0 nestes exemplos. Substitua-os por caracteres suficientes da sua [chave] pretendida que identifiquem exclusivamente o bloco.

    O arquivo ini.txt é sua entrada, ou seja:

    $ cat ini.txt 
    [Profile2]
    Name=default-test
    IsRelative=0
    Path=/home/user/ffprofiles/f9bwn86n.default-test
    
    [Profile1]
    Name=default
    IsRelative=1
    Path=x64qf7nv.default
    Default=1
    
    [Profile0]
    Name=default-release
    IsRelative=1
    Path=9hv1fbkk.default-release-3426201712696
    
    [General]
    StartWithLastProfile=1
    Version=2
    
    [Install22379532B4E49482]
    Default=9hv1fbkk.default-release-3426201712696
    Locked=1
    
    • 1
  5. Prabhjot Singh
    2024-11-19T17:28:03+08:002024-11-19T17:28:03+08:00

    Usando awk:

    $ awk 'BEGIN{RS="";ORS="\n\n"} 
      /^\[Profile/; 
      /^\[Install/ && match($0,/\nDefault=[^[:space:]]+/){
        print substr($0,RSTART,RLENGTH)
       }' file
    
    • 0
  6. Kusalananda
    2024-11-19T23:40:14+08:002024-11-19T23:40:14+08:00

    Os dois comandos a seguir são portáteis para qualquer sistema Unix que possa executar o wrapper de Andrey Kislyuk tomlqem torno do onipresente jqanalisador JSON , que acredito ser um conjunto mais amplo de sistemas do que aqueles que podem executar o Firefox.

    Minha única suposição é que qualquer string depois de a =já é uma string TOML adequadamente codificada , sem aspas duplas.

    Primeiro, a entrada pode ser convertida para o formato TOML padrão citando todos os valores:

    sed 's/=\(.*\)/="\1"/' input
    

    Podemos então extrair as Profileseções:

    sed 's/=\(.*\)/="\1"/' input |
    tomlq -t 'with_entries(select(.key | startswith("Profile")))'
    

    Dado o exemplo da questão, isso deve nos dar

    [Profile2]
    Name = "default-test"
    IsRelative = "0"
    Path = "/home/user/ffprofiles/f9bwn86n.default-test"
    
    [Profile1]
    Name = "default"
    IsRelative = "1"
    Path = "x64qf7nv.default"
    Default = "1"
    
    [Profile0]
    Name = "default-release"
    IsRelative = "1"
    Path = "9hv1fbkk.default-release-3426201712696"
    

    Se você não gostar dos espaços ao redor dos sinais de igual ou das aspas adicionadas, remova-os passando a saída por sed 's/ = "\(.*\)"/=\1/'.

    Podemos então obter separadamente o valor Installda seção Default, se existir:

    sed 's/=\(.*\)/="\1"/' input |
    tomlq -r 'with_entries(select((.key | startswith("Install")) and (.value | has("Default"))))[].Default'
    

    ... ou, se você não se importar em obter uma linha vazia para dados inexistentes, apenas

    sed 's/=\(.*\)/="\1"/' input |
    tomlq -r 'with_entries(select((.key | startswith("Install"))))[].Default'
    

    Dado o seu exemplo de entrada, isso deve fornecer o valor simples

    9hv1fbkk.default-release-3426201712696
    

    (Não podemos combinar os dois comandos, pois o TOML não pode representar não objetos no nível superior.)


    Usando essa abordagem, você pode facilmente extrair, por exemplo, a seção que tem o Pathvalor que corresponde ao valor Installda seção ( no exemplo):DefaultProfile0

    sed 's/=\(.*\)/="\1"/' input |
    tomlq -t 'with_entries(select(.key | startswith("Install")))[].Default as $default | map_values(select(.Path == $default))'
    
    • 0

relate perguntas

  • remova o número de linhas duplicadas com base na correspondência antes da primeira vírgula

  • anexar linhas após outros arquivos linha por linha

  • Como remover uma única linha entre duas linhas

  • Reorganize as letras e compare duas palavras

  • Embaralhamento de arquivo de várias linhas

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