ini 파일을 bash 배열 변수로 구문 분석하고 변환하는 방법은 무엇입니까?


13

ini 파일을 bash 배열 변수로 변환하려고합니다. 샘플 ini는 다음과 같습니다.

[foobar]
session=foo
path=/some/path

[barfoo]
session=bar
path=/some/path

그래서 이들은 다음과 같이됩니다 :

session[foobar]=foo
path[foobar]=/some/path
session[barfoo]=bar

등등.

지금은이 명령 만 내놓을 수있었습니다

awk -F'=' '{ if ($1 ~ /^\[/) section=$1; else if ($1 !~ /^$/) print $1 section "=" $2 }'

또한 다른 문제는 공간을 거의 =고려 하지 않는다는 것 입니다. sed아마도이 직업에 더 적합 하다고 생각 하지만에 섹션 이름의 임시 변수를 저장하고 저장하는 방법을 모르겠습니다 sed.

어떻게 해야할지 모르겠습니까?


이 작업을 수행하는 다른 효율적인 방법이 있다면 자유롭게 솔루션을 게시하십시오 :)
Flint


간단한 해결책을 확인하십시오 : 쉘 스크립트 내에서 INI 값을 얻는 방법은 무엇입니까? stackoverflow SE에서.
kenorb

답변:


10

Gawk는 정규식을 필드 구분자로 받아들입니다. 다음은 등호 주위의 공백을 제거하지만 나머지 줄에는 공백을 유지합니다. 값 주위에 따옴표가 추가되므로 Bash 지정이 수행 될 때 해당 공백이 유지됩니다. 섹션 이름이 숫자 변수가 될 것이라고 가정하지만 Bash 4를 사용하는 경우 섹션 이름 자체를 인덱스로 사용하여 연관 배열을 사용하도록 쉽게 조정할 수 있습니다.

awk -F ' *= *' '{ if ($1 ~ /^\[/) section=$1; else if ($1 !~ /^$/) print $1 section "=" "\"" $2 "\"" }'

Bash 변수 이름은 공백을 포함 할 수 없으므로 Khaled가 $ 1 및 섹션에만 표시하는 공백 제거를 수행 할 수도 있습니다.

또한 값에 등호가 포함되어 있으면이 방법이 작동하지 않습니다.

다른 기술은 Bash while read루프 를 사용 하고 파일을 읽을 때 할당을 수행 하는 것입니다.이 방법을 사용하면 declare대부분의 악성 콘텐츠로부터 안전합니다.

foobar=1
barfoo=2  # or you could increment an index variable each time a section is found
while IFS='= ' read var val
do
    if [[ $var == \[*] ]]
    then
        section=$var
    elif [[ $val ]]
    then
        declare "$var$section=$val"
    fi
done < filename

다시, 연관 배열은 상당히 쉽게 지원 될 수 있습니다.


1
아주 좋은 정보와 나는 특히 외부 명령에 의존하는 대신 내장 함수 bash를 사용하기 때문에 두 번째 기술을 좋아합니다.
Flint

@TonyBarganski : 하나를 다른 것으로 파이핑하는 대신 하나의 AWK 호출로 수정할 수 있습니다.
추후 공지가있을 때까지 일시 중지되었습니다.

10

INI 파서에 내장되어 있기 때문에이 작업에 간단한 파이썬 스크립트를 사용합니다 .

#!/usr/bin/env python

import sys, ConfigParser

config = ConfigParser.ConfigParser()
config.readfp(sys.stdin)

for sec in config.sections():
    print "declare -A %s" % (sec)
    for key, val in config.items(sec):
        print '%s[%s]="%s"' % (sec, key, val)

그리고 bash에서 :

#!/bin/bash

# load the in.ini INI file to current BASH - quoted to preserve line breaks
eval "$(cat in.ini  | ./ini2arr.py)"

# test it:
echo ${barfoo[session]}

물론 awk에는 짧은 구현이 있지만 더 읽기 쉽고 유지 관리하기 쉽다고 생각합니다.


3
4.2 이전의 bash 버전에서는, 연관 배열을 채우기 전에 선언해야합니다. 예 :print "declare -A %s" % (sec)
Felix Eve

2
대신 eval:source <(cat in.ini | ./ini2arr.py)
추가 통지가있을 때까지 일시 중지되었습니다.

3

여분의 공백을 제거하려면 내장 기능을 사용할 수 있습니다 gsub. 예를 들어 다음을 추가 할 수 있습니다.

gsub(/ /, "", $1);

모든 공백이 제거됩니다. 토큰의 시작 또는 끝에서 공백을 제거하려면

gsub(/^ /, "", $1);
gsub(/ $/, "", $1);

멋진 트릭. 내장 함수가 있다는 것을 몰랐습니다 :)
Flint

0

다음은 순수한 bash 솔루션입니다.

이것은 chilladx가 게시 한 새롭고 향상된 버전입니다.

https://github.com/albfan/bash-ini-parser

정말 쉽게 초기 예제를 따라하는 경우 : 당신이 다운로드 한 후, 바로 파일을 복사 bash-ini-parser하고, scripts/file.ini같은 디렉토리에, 나는뿐만 아니라 동일한 디렉토리 아래에 제공 한 예제를 사용하여 클라이언트 테스트 스크립트를 작성합니다.

source ./bash-ini-parser
cfg_parser "./file.ini"
cfg_section_sec2
echo "var2=$var2"
echo "var5[*]=${var5[*]}"
echo "var5[1]=${var5[1]}"

bash-ini-parser 스크립트에 대한 추가 개선 사항은 다음과 같습니다.

Windows 줄 끝 및 Unix로 ini 파일을 읽으려면 파일을 읽는 사람 바로 다음에 cfg_parser 함수에이 줄을 추가하십시오.

ini=$(echo "$ini"|tr -d '\r') # remove carriage returns

액세스 제한이있는 파일을 읽으려면이 선택적 기능을 추가하십시오.

# Enable the cfg_parser to read "locked" files
function sudo_cfg_parser {

    # Get the file argument
    file=$1

    # If not "root", enable the "sudo" prefix
    sudoPrefix=
    if [[ $EUID -ne 0 ]]; then sudoPrefix=sudo; fi

    # Save the file permissions, then "unlock" the file
    saved_permissions=$($sudoPrefix stat -c %a $file)
    $sudoPrefix chmod 777 $file

    # Call the standard cfg_parser function
    cfg_parser $file

    # Restore the original permissions
    $sudoPrefix chmod $saved_permissions $file  
}

때문에 공감해야했습니다 chmod 777. 최선의 방법이지만, ini 파일을 실행 가능하게 만들 필요는 없습니다. 더 나은 접근 방식은 sudo파일을 읽는 데 사용 하고 권한을 망칠 수는 없습니다.
Richlv 2016 년

@Richlv Ok. 다운 투표 설명에 감사드립니다. 그러나 이것은 이것의 작은 부분이며, 전체적으로 질문에 대답하는 한 최소한의 의미가 있습니다. "답변"은 github.com/albfan/bash-ini-parser 링크 입니다. 선택적 래퍼 함수에 이미 레이블이 지정된 것에 대해서는 전체 투표를하지 않고 편집을 제안 할 수 있습니다.
BuvinJ

0

파이썬의 ConfigParser가 항상 있다고 가정하면 다음과 같이 쉘 도우미 함수를 작성할 수 있습니다.

get_network_value()
{
    cat <<EOF | python
import ConfigParser
config = ConfigParser.ConfigParser()
config.read('network.ini')
print (config.get('$IFACE','$param'))
EOF
}

$IFACE$param각 부분 파라미터이다.

그런 다음이 도우미는 다음과 같은 호출을 허용합니다.

address=`param=address get_network_value` || exit 1
netmask=`param=netmask get_network_value` || exit 1
gateway=`param=gateway get_network_value` || exit 1

도움이 되었기를 바랍니다!


0

Git을 사용할 수 있고 키 이름에 밑줄을 사용할 수 없다는 제약 조건이있는 경우 git config범용 INI 파서 / 편집기로 사용할 수 있습니다 .

키 / 값 쌍을 구문 분석하고 =중요하지 않은 공백을 삭제하고 주석 ( ;및 및 모두 #)을 기본적으로 무료로 제공합니다. 아래에 OP의 입력 .ini및 원하는 출력 (Bash 연관 배열)에 대한 완전한 작업 예가 포함되어 있습니다.

그러나 이와 같은 구성 파일이 주어지면

; mytool.ini
[section1]
    inputdir = ~/some/dir
    enablesomefeature = true
    enablesomeotherfeature = yes
    greeting = Bonjour, Monde!

[section2]
    anothersetting = 42

… 빠르고 더러운 솔루션 만 필요하다면 Bash 연관 배열에서 설정을한다는 아이디어와 결혼하지 않아도됩니다.

eval $(git config -f mytool.ini --list | tr . _)

# or if 'eval' skeeves you out excessively
source <(git config -f mytool.ini --list | tr . _)

sectionname_variablename현재 환경에서 명명 된 환경 변수를 만듭니다 . 물론 이것은 어떤 값도 마침표 나 공백을 포함하지 않을 것이라고 믿을 수있는 경우에만 작동합니다 (보다 강력한 솔루션은 아래 참조).

다른 간단한 예

쉘 함수를 사용하여 입력을 저장하는 임의의 값 가져 오기 :

function myini() { git config -f mytool.ini; }

별칭도 여기 OK 겠지만, 사람들은 일반적으로 쉘 스크립트 [확대되지 않는 , 어쨌든 별칭은 쉘 기능에 의해 대체된다 "거의 모든 목적을 위해,"[ 2 ], 배쉬에 따라 man 페이지 .

myini --list
# result:
# section1.inputdir=~/some/dir
# section1.enablesomefeature=true
# section1.enablesomeotherfeature=yes
# section2.anothersetting=42

myini --get section1.inputdir
# result:
# ~/some/dir

--type옵션을 사용하면 특정 설정을 정수, 부울 또는 경로로 "정식화"할 수 있습니다 (자동 확장 ~).

myini --get --type=path section1.inputdir  # value '~/some/dir'
# result:
# /home/myuser/some/dir

myini --get --type=bool section1.enablesomeotherfeature  # value 'yes'
# result:
# true

약간 더 강력하고 빠르고 더러운 예

키 값의 내부 공백을 유지하면서 현재 환경에서와 mytool.ini같이 모든 변수를 사용 가능하게 SECTIONNAME_VARIABLENAME하십시오.

source <(
    git config -f mytool.ini --list \
      | sed 's/\([^.]*\)\.\(.*\)=\(.*\)/\U\1_\2\E="\3"/'
)

sed 표현이 영어로하는 것은

  1. 기억 기간에 비 기간 문자의 무리를 찾는 등 그 \1다음,
  2. 가로 기억, 등호까지 문자의 무리를 발견 \2하고,
  3. 등호 뒤의 모든 문자 찾기 \3
  4. 마지막으로 대체 문자열에서
    • 섹션 이름 + 변수 이름은 대문자이며
    • 따옴표로 묶지 않은 경우 쉘에 특별한 의미가있는 문자가 포함 된 경우 값 부분은 큰 따옴표로 묶습니다 (공백과 같이).

대체 문자열 ( \U\E대체 문자열의 해당 부분을 대문자로 표시) 의 및 시퀀스는 GNU sed확장입니다. macOS 및 BSD에서는 여러 -e효과를 사용 하여 동일한 효과를 얻을 수 있습니다.

섹션 이름 에 포함 된 따옴표와 공백을 다루는 것은 git config독자에게 연습으로 남겨둔다.:)

섹션 이름을 Bash 연관 배열의 키로 사용

주어진:

; foo.ini
[foobar]
session=foo
path=/some/path

[barfoo]
session=bar
path=/some/path

이것은 OP가 요구하는 결과를 sed replacement 표현으로 간단히 캡처하여 재 배열하고 GNU sed 없이도 잘 작동합니다.

source <(
    git config -f foo.ini --list \
      | sed 's/\([^.]*\)\.\(.*\)=\(.*\)/declare -A \2["\1"]="\3"/'
)

실제 .ini파일 을 인용하는 데 어려움이있을 수 있지만 제공된 예제에서는 작동합니다. 결과:

declare -p {session,path}
# result:
# declare -A session=([barfoo]="bar" [foobar]="foo" )
# declare -A path=([barfoo]="/some/path" [foobar]="/some/path" )
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.