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.[Profile
n
]
n
Default
[Install*]
Default
grep -E 'Default=[^1]' profiles.ini
grep -A4 '^\[Profile' profiles.ini
Name
IsRelative
Path
Default
-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 é 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:
Como um único script awk, isso pode funcionar:
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.)
Usando qualquer awk:
O que você está descrevendo é um formato de arquivo de texto estruturado com contexto.
awk
pode (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
,sed
ou 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 umawk
analisador 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. Oexamples/
diretório tem dois exemplos que você pode adaptar diretamente ao seu caso de uso; basta substituirtoml_parse(char*,…)
portoml_parse_file(FILE*,…)
, e abrir o arquivo que você recebeu comoargv[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
awk
pré-instalado muito provavelmente também tem umpython
.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é queProfileN+1
nã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: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]:
ou extrair o bloco que começa com [Geral]:
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:
Usando
awk
:Os dois comandos a seguir são portáteis para qualquer sistema Unix que possa executar o wrapper de Andrey Kislyuk
tomlq
em torno do onipresentejq
analisador 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:
Podemos então extrair as
Profile
seções:Dado o exemplo da questão, isso deve nos dar
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
Install
da seçãoDefault
, se existir:... ou, se você não se importar em obter uma linha vazia para dados inexistentes, apenas
Dado o seu exemplo de entrada, isso deve fornecer o valor simples
(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
Path
valor que corresponde ao valorInstall
da seção ( no exemplo):Default
Profile0