Eu olhei alguns scripts de análise do bash ini e viesseum usado algumas vezes aqui, então estou tentando ver se vai funcionar para mim. Parece que ele lê o arquivo ini linha por linha várias vezes e, a cada passagem, constrói progressivamente uma função que finalmente é avaliada. Funciona bem para alguns caracteres especiais, mas não para outros. Se um valor no arquivo contiver aspas simples ou símbolo maior/menor que, o script retornará erros de sintaxe. Outros símbolos também criam resultados inesperados. Como posso lidar com esses caracteres conforme são encontrados?
Esta é a função que analisa o ini.
#!/usr/bin/env bash
cfg_parser ()
{
ini="$(<$1)" # read the file
ini="${ini//[/\[}" # escape [
ini="${ini//]/\]}" # escape ]
IFS=$'\n' && ini=( ${ini} ) # convert to line-array
ini=( ${ini[*]//;*/} ) # remove comments with ;
ini=( ${ini[*]/\ =/=} ) # remove tabs before =
ini=( ${ini[*]/=\ /=} ) # remove tabs be =
ini=( ${ini[*]/\ =\ /=} ) # remove anything with a space around =
ini=( ${ini[*]/#\\[/\}$'\n'cfg.section.} ) # set section prefix
ini=( ${ini[*]/%\\]/ \(} ) # convert text2function (1)
ini=( ${ini[*]/=/=\( } ) # convert item to array
ini=( ${ini[*]/%/ \)} ) # close array parenthesis
ini=( ${ini[*]/%\\ \)/ \\} ) # the multiline trick
ini=( ${ini[*]/%\( \)/\(\) \{} ) # convert text2function (2)
ini=( ${ini[*]/%\} \)/\}} ) # remove extra parenthesis
ini[0]="" # remove first element
ini[${#ini[*]} + 1]='}' # add the last brace
eval "$(echo "${ini[*]}")" # eval the result
}
arquivo ini
[Section1]
value1=abc`def # unexpected EOF while looking for matching ``'
value2=ghi>jkl # syntax error near unexpected token `>'
value3=mno$pqr # executes ok but outputs "mnoqr"
value4=stu;vwx # executes ok but outputs "stu"
Responder1
O fato de vocêpodefazer algo bash
não significa que vocêdeve.
sh
(e bash
etc.) scripts são mais adequados para serem wrappers relativamente simples para iniciar programas ou em torno de comandos de processamento de texto. Para tarefas mais complicadas, incluindo analisar arquivos ini e agir sobre eles, outras linguagens são mais apropriadas. Você já pensou em escrever seu roteiro em perl
ou python
? Ambos têm bons analisadores de arquivos .ini - usei Config::INI
o módulo do Perl várias vezes quando precisei analisar um arquivo ini.
Mas se você insiste em fazer isso no bash, você deve usar um array associativo em vez de definir variáveis individuais.
Comece com algo assim:
#! /bin/bash
inifile='user1074170.ini'
# declare $config to be an associative array
declare -A config
while IFS='=' read -r key val ; do
config["$key"]="$val"
done < <(sed -E -e '/^\[/d
s/#.*//
s/[[:blank:]]+$|^[[:blank:]]+//g' "$inifile" )
# now print out the config array
set | grep '^config='
O sed
script exclui a [Section1]
linha (na verdade, todas as linhas que começam com um colchete aberto [
- você desejará lidar com isso de maneira diferente [1] em um arquivo ini com múltiplas seções) e remove comentários, bem como espaços em branco iniciais e finais. O while
loop lê cada linha, usando =
como delimitador de campo, e atribui o conteúdo às variáveis $key e $val, que são então adicionadas ao array $config.
Saída:
config=([value1]="abc\`def" [value3]="mno\$pqr" [value2]="ghi>jkl" [value4]="stu;vwx" )
Você pode usar as entradas do array posteriormente em seu script assim:
$ echo value1 is "${config[value1]}"
value1 is abc`def
$ [ "${config[value4]}" = 'stu;vwx' ] && echo true
true
[1] awk
ou perl
possui maneiras convenientemente fáceis de ler arquivos no modo "parágrafo". Um parágrafo é definido como um bloco de texto separado de outros blocos de texto por uma ou mais linhas em branco.
por exemplo, para trabalhar apenas com [Section1]
, insira o awk
script abaixo imediatamente antes de o sed
script alimentar o while
loop acima:
awk -v RS= -v ORS='\n\n' '/\[Section1\]/' "$inifile" | sed ...
(e remova "$inifile"
do final da sed
linha de comando, é claro - você não deseja alimentar o arquivo novamente depois de se dar ao trabalho de extrair apenas [Section1]
dele).
A configuração ORS
não é estritamente necessária se você estiver extraindo apenas uma seção do arquivo ini - mas será útil para manter a separação dos parágrafos se você estiver extraindo duas ou mais seções.
Responder2
Eu sei que é uma resposta incompleta, mas o MySQL.lns
in augeas parece ser capaz de analisar a maior parte disso. Em augtool
:
augtool> set /augeas/load/testini/incl "/root/test.ini"
augtool> set /augeas/load/testini/lens "MySQL.lns"
augtool> load
augtool> ls /files/root/
.ssh/ test.ini/
augtool> ls /files/root/test.ini
target/ = Section1
augtool> ls /files/root/test.ini/target/
value1/ = abc`def
value2/ = ghi>jkl
value3/ = mno$pqr
value4/ = stu
O único que estragou foi o último e, TBH, não acho que seja um erro. Nos .ini
arquivos, o ponto e vírgula marca o início de um comentário. Também gostaria de perguntar se seus dados realmente são assim.
Se isso acontecer, você pode apenas fazer algo sed
antes que defina ;
algum valor de caractere não utilizado e depois transformá-lo novamente no pós-processamento. Em última análise, você precisará de alguns padrões para que o arquivo seja capaz de ter qualquer estrutura discernível.
EDITAR:
Eu testei com as lentes do PHP e consegui tudo, desde que os valores fossem citados:
[root@vlzoreman ~]# augtool
augtool> set /augeas/load/testini/lens "PHP.lns"
augtool> set /augeas/load/testini/incl "/root/test.ini"
augtool> load
augtool> ls /files/root/test.ini/Section1/
value1 = abc`def
value2 = ghi>jkl
value3 = mno$pqr
value4 = stu;vwx
Caso contrário, foi tão longe quanto as lentes do MySQL.
EDITAR #2:
Tenho certeza de que há uma maneira mais limpa de escrever isso, mas este é um exemplo de uso:
[root@vlp-foreman ~]# bash bash.sh
Values for: Section1:
:: value1 is abc`def
:: value2 is ghi>jkl
:: value3 is mno$pqr
:: value4 is stu;vwx
Values for: Section2:
:: value1 is abc`def
O roteiro é:
#!/bin/bash
sections=$(augtool -A --transform "PHP.lns incl /root/test.ini" ls /files/root/test.ini | cut -f1 -d/)
for currentSection in $sections; do
echo "Values for: $currentSection:"
fields=$(augtool -A --transform "PHP.lns incl /root/test.ini" ls /files/root/test.ini/$currentSection | awk '{print $1}')
for currentField in $fields; do
currentValue=$(augtool -A --transform "PHP.lns incl /root/test.ini" print /files/root/test.ini/$currentSection/$currentField | cut -f2 -d=)
currentValue=$(echo $currentValue | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' | sed -e 's/^"//' -e 's/"$//')
echo -e "\t:: $currentField is $currentValue"
done
done
Responder3
dê uma olhada em crudini
:https://www.pixelbeat.org/programs/crudini/
no Ubuntu você pode instalá-lo para sudo apt install crudini
ler um valor do arquivo ini, execute:
$ value1=$(crudini --get "crudini.ini" "Section1" "value1")
crudini suporta vários formatos de arquivos ini e lida com caracteres especiais:
crudini.ini
[Section1]
value1=abc`def
value2=ghi>jkl
value3=mno$pqr
value4=stu;vwx
lendo valores
$ for i in {1..4}; do crudini --get "crudini.ini" "Section1" "value$i"; done
abc`def
ghi>jkl
mno$pqr
stu;vwx
$