쉘 스크립트에 구성 파일 사용


26

내 스크립트에 대한 구성 파일을 만들어야합니다. 여기 예가 있습니다.

스크립트:

#!/bin/bash
source /home/myuser/test/config
echo "Name=$nam" >&2
echo "Surname=$sur" >&2

내용 /home/myuser/test/config:

nam="Mark"
sur="Brown"

작동합니다!

내 질문 : 이것이 올바른 방법입니까 아니면 다른 방법이 있습니까?


변수는 맨 위에 있어야합니다. 나는 그것이 작동하는 것에 놀랐습니다. 어쨌든 구성 파일이 필요한 이유는 무엇입니까? 이 변수를 다른 곳에서 사용할 계획입니까?
Faheem Mitha

Faheem, 스크립트에는 많은 옵션이 있으므로 변수가 필요합니다. 구성 파일을 사용하면 스크립트가 단순화됩니다. 감사합니다
Pol Hallen

5
이모 괜찮아. 나는 이렇게 할 것입니다.
Tinti

abcde또한이 방법을 수행하며 상당히 큰 프로그램입니다 (쉘 스크립트). 여기서 볼 수 있습니다 .
Lucas

답변:


19

source임의의 코드를 실행하므로 안전하지 않습니다. 이는 걱정할 사항이 아니지만 파일 사용 권한이 올바르지 않은 경우 파일 시스템 액세스 권한이있는 공격자가 다음과 같이 보안이 설정된 스크립트에 의해로드 된 구성 파일에 코드를 삽입하여 권한있는 사용자로 코드를 실행할 수 있습니다. 초기화 스크립트.

지금까지 내가 찾은 최고의 솔루션은 서투른 재발견 솔루션입니다.

myscript.conf

password=bar
echo rm -rf /
PROMPT_COMMAND='echo "Sending your last command $(history 1) to my email"'
hostname=localhost; echo rm -rf /

을 사용 하면 실행중인 사용자의 변경뿐만 아니라 두 번 source실행 echo rm -rf /됩니다 $PROMPT_COMMAND. 대신 다음을 수행하십시오.

myscript.sh (배시 4)

#!/bin/bash
typeset -A config # init array
config=( # set default values in config array
    [username]="root"
    [password]=""
    [hostname]="localhost"
)

while read line
do
    if echo $line | grep -F = &>/dev/null
    then
        varname=$(echo "$line" | cut -d '=' -f 1)
        config[$varname]=$(echo "$line" | cut -d '=' -f 2-)
    fi
done < myscript.conf

echo ${config[username]} # should be loaded from defaults
echo ${config[password]} # should be loaded from config file
echo ${config[hostname]} # includes the "injected" code, but it's fine here
echo ${config[PROMPT_COMMAND]} # also respects variables that you may not have
               # been looking for, but they're sandboxed inside the $config array

myscript.sh (Mac / Bash 3 호환)

#!/bin/bash
config() {
    val=$(grep -E "^$1=" myscript.conf 2>/dev/null || echo "$1=__DEFAULT__" | head -n 1 | cut -d '=' -f 2-)

    if [[ $val == __DEFAULT__ ]]
    then
        case $1 in
            username)
                echo -n "root"
                ;;
            password)
                echo -n ""
                ;;
            hostname)
                echo -n "localhost"
                ;;
        esac
    else
        echo -n $val
    fi
}

echo $(config username) # should be loaded from defaults
echo $(config password) # should be loaded from config file
echo $(config hostname) # includes the "injected" code, but it's fine here
echo $(config PROMPT_COMMAND) # also respects variables that you may not have
               # been looking for, but they're sandboxed inside the $config array

내 코드에서 보안 취약점을 발견하면 회신하십시오.


1
참고로이 슬프게도이 적용되는 배쉬 버전 4.0 솔루션을 미친 라이센스 문제 애플에 의해 부과와 Mac에서 기본적으로 사용할 수 없습니다
Sukima

@Sukima 좋은 지적. Bash 3과 호환되는 버전을 추가했습니다. 약점은 *입력을 올바르게 처리하지 못하지만 Bash에서 해당 문자를 잘 처리한다는 것입니다.
Mikkel

비밀번호에 백 슬래시가 포함 된 경우 첫 번째 스크립트가 실패합니다.
Kusalananda

@Kusalananda 백 슬래시를 이스케이프 처리하면 어떻게됩니까? my\\password
Mikkel

10

Mac 및 Linux 모두에서 Bash 3 이상과 호환되는 깨끗하고 이식 가능한 버전이 있습니다.

모든 셸 스크립트에서 복잡하고 복잡한 중복 된 "기본"구성 기능이 필요하지 않도록 모든 기본값을 별도의 파일로 지정합니다. 또한 기본 폴백을 사용하거나 사용하지 않고 읽을 수 있습니다.

config.cfg :

myvar=Hello World

config.cfg.defaults :

myvar=Default Value
othervar=Another Variable

config.shlib (이것은 라이브러리이므로 shebang-line은 없습니다) :

config_read_file() {
    (grep -E "^${2}=" -m 1 "${1}" 2>/dev/null || echo "VAR=__UNDEFINED__") | head -n 1 | cut -d '=' -f 2-;
}

config_get() {
    val="$(config_read_file config.cfg "${1}")";
    if [ "${val}" = "__UNDEFINED__" ]; then
        val="$(config_read_file config.cfg.defaults "${1}")";
    fi
    printf -- "%s" "${val}";
}

test.sh (또는 구성 값을 읽으려는 스크립트) :

#!/usr/bin/env bash
source config.shlib; # load the config library functions
echo "$(config_get myvar)"; # will be found in user-cfg
printf -- "%s\n" "$(config_get myvar)"; # safer way of echoing!
myvar="$(config_get myvar)"; # how to just read a value without echoing
echo "$(config_get othervar)"; # will fall back to defaults
echo "$(config_get bleh)"; # "__UNDEFINED__" since it isn't set anywhere

테스트 스크립트 설명 :

  • 참고 모든 test.sh에서 config_get의 용도는 따옴표에 싸여있다. 모든 config_get을 큰 따옴표로 묶어 변수 값의 텍스트 를 플래그로 잘못 해석 하지 않도록합니다 . 또한 구성 값의 행에 여러 공백과 같은 공백을 올바르게 유지합니다.
  • 그리고 그 printf라인은 무엇입니까? 글쎄, 그것은 당신이 알아야 할 것입니다 : echo당신이 통제 할 수없는 텍스트를 인쇄하기위한 나쁜 명령입니다. 큰 따옴표를 사용하더라도 플래그를 해석합니다. myvar(in config.cfg)을 (를 ) 설정 -e하면 빈 줄 echo이 표시됩니다. 이는 플래그라고 생각 하기 때문 입니다. 그러나 printf그 문제는 없습니다. 는 printf --"이 인쇄 및 플래그 등 아무것도 해석하지 않는다"라는, 그리고는 "%s\n"뒤에 개행 문자의 문자열로 출력 형식 "라고, 그리고 마지막으로 최종 매개 변수는 형식의 printf의 값입니다.
  • 화면에 값을 표시하지 않으려면 다음과 같이 정상적으로 값을 지정하면됩니다 myvar="$(config_get myvar)";. 화면에 인쇄하려는 경우 printf를 사용하여 사용자 구성에있을 수있는 에코 호환되지 않는 문자열에 대해 완전히 안전합니다. 그러나 사용자가 제공 한 변수 반향하고있는 문자열의 첫 번째 문자 가 아닌 경우 반향은 괜찮습니다 . "플래그"가 해석 될 수있는 유일한 상황이므로 echo "foo: $(config_get myvar)";"foo"는 그렇지 않기 때문에 안전합니다. 대시로 시작하므로 나머지 문자열도 플래그가 아니라고 에코에 알립니다. :-)

@ user2993656 원래 코드에 개인 설정 파일 이름 (environment.cfg)이 올바른 파일 대신 여전히 있다는 것을 알게되어 감사합니다. "echo -n"편집은 사용 된 쉘에 따라 다릅니다. Mac / Linux Bash에서 "echo -n"은 "줄 바꿈없는 에코"를 의미하며, 줄 바꿈을 방지합니다. 그러나 그것 없이는 똑같이 작동하는 것처럼 보입니다. 편집에 감사드립니다!
gw0

실제로, 그냥 에코 대신에 printf를 사용하도록 다시 작성하여 구성 값에서 에코가 "플래그"를 잘못 해석하는 위험을 제거 할 수 있도록했습니다.
gw0

나는이 버전을 정말로 좋아한다. 나는 config.cfg.defaults전화 할 때 그것들을 정의하는 대신에 포기했다 $(config_get var_name "default_value"). tritarget.org/static/…
Sukima

7

구성 파일을 구문 분석하고 실행하지 마십시오.

현재 매우 간단한 XML 구성을 사용하는 직장에서 응용 프로그램을 작성 중입니다.

<config>
    <username>username-or-email</username>
    <password>the-password</password>
</config>

쉘 스크립트 ( "응용 프로그램")에서 이것은 사용자 이름을 얻기 위해 수행하는 것입니다 (더 많거나 적은, 쉘 함수에 넣었습니다).

username="$( xml sel -t -v '/config/username' "$config_file" )"

xml명령은 대부분의 Unices 에서 사용할 수있는 XMLStarlet 입니다.

응용 프로그램의 다른 부분도 XML 파일로 인코딩 된 데이터를 처리하므로 XML을 사용하므로 가장 쉽습니다.

JSON을 선호한다면 jq쉘 JSON 파서를 사용하기 쉽습니다.

내 구성 파일은 JSON에서 다음과 같습니다.

{                                 
  "username": "username-or-email",
  "password": "the-password"      
}                

그런 다음 스크립트에서 사용자 이름을 얻습니다.

username="$( jq -r '.username' "$config_file" )"

스크립트를 실행하면 많은 장점과 단점이 있습니다. 주요 단점은 보안입니다. 누군가 구성 파일을 변경할 수 있으면 코드를 실행할 수 있으며,이를 바보 증거로 만들기가 더 어렵습니다. 장점은 속도이며, 간단한 테스트에서 pq를 실행하는 것보다 구성 파일을 소싱하는 것이 10,000 배 이상 빠르며 원숭이 패치 파이썬을 좋아하는 사람이라면 누구나이 점을 인정할 것입니다.
이카루스

@icarus 일반적으로 얼마나 큰 구성 파일이 발생하며 한 세션에서 얼마나 자주 구성 파일을 구문 분석해야합니까? 여러 값이 XML 또는 JSON에서 한 번에 사용되었을 수 있습니다.
Kusalananda

일반적으로 소수 (1 ~ 3)의 값만 있습니다. eval여러 값을 설정하는 데 사용 하는 경우 구성 파일의 선택된 부분을 실행하고 있습니다 :-).
이카루스

1
@icarus 나는 배열을 생각하고 있었다 ... eval아무것도 필요가 없습니다 . 기존 파서에서 표준 형식을 사용할 때 (외부 유틸리티 임에도 불구하고) 성능 저하는 견고성, 코드 양, 사용 편의성 및 유지 관리 성과 비교할 때 무시할 만합니다.
Kusalananda

1
"설정 파일을 파싱하고 실행하지 마십시오"+1
Iiridayn

4

가장 일반적인, 효율적이고 올바른 방법은 사용하는 것입니다 source, 또는 .속기의 형태로. 예를 들면 다음과 같습니다.

source /home/myuser/test/config

또는

. /home/myuser/test/config

그러나 고려해야 할 사항은 추가 코드를 삽입 할 수있는 경우 외부에서 제공하는 추가 구성 파일을 사용하면 발생할 수있는 보안 문제입니다. 이 문제를 감지하고 해결하는 방법에 대한 자세한 내용은 http://wiki.bash-hackers.org/howto/conffile#secure_it 의 '보안'섹션을 참조하십시오.


5
나는 그 기사에 대한 높은 희망을 가지고 있었지만 (나의 검색 결과에도 나왔음), 정규식을 사용하여 악성 코드를 걸러 내려는 제안은 무용지물이다.
Mikkel

점이있는 절차에는 절대 경로가 필요합니까? 상대와 함께 작동하지 않습니다
Davide

2

나는 이것을 내 스크립트에서 사용한다 :

sed_escape() {
  sed -e 's/[]\/$*.^[]/\\&/g'
}

cfg_write() { # path, key, value
  cfg_delete "$1" "$2"
  echo "$2=$3" >> "$1"
}

cfg_read() { # path, key -> value
  test -f "$1" && grep "^$(echo "$2" | sed_escape)=" "$1" | sed "s/^$(echo "$2" | sed_escape)=//" | tail -1
}

cfg_delete() { # path, key
  test -f "$1" && sed -i "/^$(echo $2 | sed_escape).*$/d" "$1"
}

cfg_haskey() { # path, key
  test -f "$1" && grep "^$(echo "$2" | sed_escape)=" "$1" > /dev/null
}

키가 가질 수없는 것을 제외하고는 모든 문자 조합을 지원해야합니다 =. 다른 것은 작동합니다.

% cfg_write test.conf mykey myvalue
% cfg_read test.conf mykey
myvalue
% cfg_delete test.conf mykey
% cfg_haskey test.conf mykey || echo "It's not here anymore"
It's not here anymore

또한 어떤 종류의 source/도 사용하지 않기 때문에 완전히 안전합니다.eval


0

내 시나리오에서 source또는 .괜찮 았지만 FOO=bar myscript.sh구성된 변수보다 우선 하는 로컬 환경 변수 (예 :)를 지원하고 싶었습니다 . 또한 구성 파일을 소스 파일 구성에 익숙한 사용자가 편집하고 편안하게 사용할 수 있기를 원했으며 가능한 한 작거나 단순하게 유지하여 매우 작은 스크립트의 주요 추력을 방해하지 않기를 원했습니다.

이것이 내가 생각해 낸 것입니다.

CONF=${XDG_CONFIG_HOME:-~/config}/myscript.sh
if [ ! -f $CONF ]; then
    cat > $CONF << CONF
VAR1="default value"
CONF
fi
. <(sed 's/^\([^=]\+\) *= *\(.*\)$/\1=${\1:-\2}/' < $CONF)

본질적으로-공백에 대해 매우 유연하지 않은 변수 정의를 확인하고 값을 해당 변수의 기본값으로 변환하고 위 변수와 같이 변수가 발견되면 수정되지 않도록 해당 행을 다시 씁니다 XDG_CONFIG_HOME. 이 변경된 버전의 구성 파일을 소싱하고 계속 진행합니다.

미래의 작업은 sed스크립트를보다 견고 하게하고 이상한 모양이나 정의가 아닌 줄을 필터링하여 줄 끝에서 끊지 않을 수는 있지만 현재로서는 충분합니다.


0

간결하고 안전합니다.

# Read common vars from common.vars
# the incantation here ensures (by env) that only key=value pairs are present
# then declare-ing the result puts those vars in our environment 
declare $(env -i `cat common.vars`)

-i통해 변수를 얻을 수 있습니다.common.vars


-2

넌 할 수있어:

#!/bin/bash
name="mohsen"
age=35
cat > /home/myuser/test/config << EOF
Name=$name
Age=$age
EOF
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.