쉘 변수를 직렬화하는 방법이 있습니까? 내가 변수를 가지고 있고 $VAR
파일이나 다른 것에 저장 한 다음 나중에 다시 읽어서 같은 값을 얻을 수 있기를 원합니까?
이 작업을 수행하는 휴대용 방법이 있습니까? (나는 그렇게 생각하지 않습니다)
bash 또는 zsh에서 수행하는 방법이 있습니까?
쉘 변수를 직렬화하는 방법이 있습니까? 내가 변수를 가지고 있고 $VAR
파일이나 다른 것에 저장 한 다음 나중에 다시 읽어서 같은 값을 얻을 수 있기를 원합니까?
이 작업을 수행하는 휴대용 방법이 있습니까? (나는 그렇게 생각하지 않습니다)
bash 또는 zsh에서 수행하는 방법이 있습니까?
답변:
경고 : 이러한 솔루션 중 하나를 사용하면 스크립트에서 셸 코드로 실행되므로 데이터 파일의 무결성을 안전하게 유지해야합니다. 그것들의 보안은 스크립트 보안에 가장 중요합니다!
예, bash와 zsh에서 typeset
내장 및 -p
인수를 사용하여 쉽게 검색 할 수있는 방식으로 변수의 내용을 직렬화 할 수 있습니다 . 출력 형식은 단순히 source
출력물을 가져 오기 위해 간단히 출력 할 수있는 형식입니다 .
# You have variable(s) $FOO and $BAR already with your stuff
typeset -p FOO BAR > ./serialized_data.sh
나중에 스크립트 또는 다른 스크립트에서 다음과 같이 물건을 다시 가져올 수 있습니다.
# Load up the serialized data back into the current shell
source serialized_data.sh
이것은 다른 쉘간에 데이터를 전달하는 것을 포함하여 bash, zsh 및 ksh에서 작동합니다. Bash는 이것을 내장 declare
함수로 변환 typeset
하지만 zsh는 이것을 구현 하지만 bash에는 typeset
ksh 호환성을 위해 여기에서 사용할 수있는 별칭이 있습니다.
위의 구현은 실제로 간단하지만 자주 호출하면 유틸리티 기능을 제공하여 더 쉽게 만들 수 있습니다. 또한 위의 사용자 정의 함수에 위의 내용을 포함하려고하면 가변 범위 지정 문제가 발생합니다. 이 버전은 이러한 문제를 제거해야합니다.
/ 상호 호환성을 zsh을 배쉬을 유지하기 위해 다음의 모든 주, 우리는 두 가지 모두의 경우 고정됩니다 typeset
와 declare
코드 중 하나 또는 둘 모두 쉘에서 작동한다 정도. 이것은 하나의 쉘 또는 다른 쉘에 대해서만이 작업을 수행하는 경우 제거 될 수있는 대량 및 혼란을 추가합니다.
이를 위해 함수를 사용하거나 다른 함수에 코드를 포함하는 데있어 주요 문제점은 typeset
함수 내부에서 스크립트로 다시 소스를 제공 할 때 전역 변수가 아닌 로컬 변수를 작성하도록 기본 설정하는 코드를 생성하는 함수입니다.
이것은 여러 해킹 중 하나로 해결할 수 있습니다. 이 문제를 해결하려는 초기 시도는 직렬화 프로세스의 출력을 구문 분석 sed
하여 -g
플래그 를 추가 하여 작성된 코드가 다시 소스 될 때 전역 변수를 정의하도록 플래그 를 추가하는 것 입니다.
serialize() {
typeset -p "$1" | sed -E '0,/^(typeset|declare)/{s/ / -g /}' > "./serialized_$1.sh"
}
deserialize() {
source "./serialized_$1.sh"
}
펑키 sed
표현식은 'typeset'또는 'declare'의 첫 번째 항목 만 일치시키고 첫 -g
번째 인수로 추가 해야합니다. Stéphane Chazelas 가 주석에서 올바르게 지적 했으므로 직렬화 된 문자열에 리터럴 줄 바꿈이 있고 선언 또는 조판이라는 단어가 포함 된 경우와도 일치 하기 때문에 첫 번째 항목 만 일치해야합니다 .
내 초기 파싱 가짜 pas 를 수정하는 것 외에도 Stéphane 은 문자열 파싱 문제를 회피 할뿐만 아니라 래퍼 함수를 사용하여 작업을 재정 의하여 추가 기능을 추가하는 유용한 후크가 될 수있는 덜 취약한 방법을 제안 했습니다. 선언 또는 조판 명령으로 다른 게임을하고 있지 않다고 가정하지만,이 기능을 자신의 다른 기능의 일부로 포함하는 상황에서는이 기술을 더 쉽게 구현할 수 있습니다. 작성중인 데이터와 -g
플래그 추가 여부를 제어 할 수 없었습니다 . 별명으로도 비슷한 작업을 수행 할 수 있습니다 . 구현에 대한 Gilles의 답변 을 참조하십시오 .
결과를 더욱 유용하게 만들기 위해 인수 배열의 각 단어가 변수 이름이라고 가정하여 함수에 전달 된 여러 변수를 반복 할 수 있습니다. 결과는 다음과 같습니다.
serialize() {
for var in $@; do
typeset -p "$var" > "./serialized_$var.sh"
done
}
deserialize() {
declare() { builtin declare -g "$@"; }
typeset() { builtin typeset -g "$@"; }
for var in $@; do
source "./serialized_$var.sh"
done
unset -f declare typeset
}
어느 솔루션을 사용하든 사용법은 다음과 같습니다.
# Load some test data into variables
FOO=(an array or something)
BAR=$(uptime)
# Save it out to our serialized data files
serialize FOO BAR
# For testing purposes unset the variables to we know if it worked
unset FOO BAR
# Load the data back in from out data files
deserialize FOO BAR
echo "FOO: $FOO\nBAR: $BAR"
declare
는 IS bash
당량 ksh
의이 typeset
. bash
, zsh
또한 지원 typeset
하는 점에서, 그래서 typeset
휴대 성이다. export -p
POSIX이지만 인수를 취하지 않으며 출력은 셸에 따라 다릅니다 (POSIX 셸에 대해 잘 지정되어 있기 때문에 bash 또는 ksh가로 호출되는 경우 sh
). 변수를 인용하십시오; split + glob 연산자를 사용하는 것은 의미가 없습니다.
-E
일부 BSD의에서 발견된다 sed
. 변수 값은 개행 문자를 포함 할 수 있으므로 sed 's/^.../.../'
올바르게 작동하지 않을 수 있습니다 .
a=$'foo\ndeclare bar' bash -c 'declare -p a'
시작하는 줄을 출력합니다 declare
. declare() { builtin declare -g "$@"; }
전화하기 전에 source
(그리고 나중에 설정을 해제 하기 전에)하는 것이 좋습니다
shopt -s expandalias
하면 대화식 이 아닌 경우 를 수행해야합니다 . 함수를 사용하면 declare
래퍼를 향상시켜 지정한 변수 만 복원 할 수 있습니다.
POSIX 쉘에서 모든 환경 변수를로 직렬화 할 수 있습니다 export -p
. 익스포트되지 않은 쉘 변수는 포함되지 않습니다. 동일한 셸에서 다시 읽고 정확히 동일한 변수 값을 얻을 수 있도록 출력이 올바르게 인용됩니다. 다른 쉘에서는 출력을 읽을 수 없습니다. 예를 들어 ksh는 POSIX 이외의 $'…'
구문을 사용 합니다.
save_environment () {
export -p >my_environment
}
restore_environment () {
. ./my_environment
}
Ksh (pdksh / mksh 및 ATT ksh), bash 및 zsh는 typeset
내장 기능으로 더 나은 기능을 제공합니다. typeset -p
정의 된 모든 변수와 해당 값을 인쇄합니다 (zsh는로 숨겨진 변수 값을 생략 함 typeset -H
). 출력에는 환경 변수를 다시 읽을 때 내보내 지지만 (읽을 때 변수를 이미 내 보낸 경우 내 보내지 않음) 적절한 선언이 포함되므로 배열은 배열 등으로 다시 읽습니다. 올바르게 인용되지만 동일한 쉘에서 읽을 수 있도록 보장됩니다. 명령 행에서 일련의 변수를 전달하여 직렬화 할 수 있습니다. 변수를 전달하지 않으면 모두 직렬화됩니다.
save_some_variables () {
typeset -p VAR OTHER_VAR >some_vars
}
bash 및 zsh에서는 함수 typeset
내부의 명령문이 해당 함수의 범위에 있으므로 함수 에서 복원을 수행 할 수 없습니다 . . ./some_vars
내보낼 때 전역 변수가 전역 변수로 다시 선언되도록주의하면서 변수 값을 사용하려는 컨텍스트에서 실행해야합니다 . 함수 내에서 값을 다시 읽고 내보내려면 임시 별명 또는 함수를 선언 할 수 있습니다. zsh에서 :
restore_and_make_all_global () {
alias typeset='typeset -g'
. ./some_vars
unalias typeset
}
bash에서 (가 declare
아닌 사용 typeset
) :
restore_and_make_all_global () {
alias declare='declare -g'
shopt -s expand_aliases
. ./some_vars
unalias declare
}
ksh typeset
에서로 정의 된 함수에서 지역 변수를 선언 function function_name { … }
하고로 정의 된 함수에서 전역 변수를 선언하십시오 function_name () { … }
.
더 많은 제어를 원하면 변수 내용을 수동으로 내보낼 수 있습니다. 변수의 내용을 정확히 파일로 인쇄하려면 printf
내장을 사용하십시오 ( 일부 쉘과 echo
같은 몇 가지 특수한 경우 echo -n
가 있고 개행을 추가 함).
printf %s "$VAR" >VAR.content
$(cat VAR.content)
명령 대체가 후행 줄 바꿈을 제거한다는 점을 제외하고는 이것을 사용하여 다시 읽을 수 있습니다 . 이 주름을 피하려면 출력이 줄 바꿈으로 끝나지 않도록 준비하십시오.
VAR=$(cat VAR.content && echo a)
if [ $? -ne 0 ]; then echo 1>&2 "Error reading back VAR"; exit 2; fi
VAR=${VAR%?}
여러 변수를 인쇄하려면 작은 따옴표로 인용하고 포함 된 모든 작은 따옴표를로 바꿉니다 '\''
. 이 형식의 인용문은 Bourne / POSIX 스타일 쉘로 다시 읽을 수 있습니다. 다음 스 니펫은 모든 POSIX 셸에서 작동합니다. 문자열 변수 (및 문자열로 다시 읽을 수는 있지만 쉘이있는 쉘의 숫자 변수)에서만 작동하지만 쉘이있는 쉘의 배열 변수는 처리하지 않습니다.
serialize_variables () {
for __serialize_variables_x do
eval "printf $__serialize_variables_x=\\'%s\\'\\\\n \"\$${__serialize_variables_x}\"" |
sed -e "s/'/'\\\\''/g" -e '1 s/=.../=/' -e '$ s/...$//'
done
}
하위 프로세스를 포크하지 않지만 문자열 조작에 더 무거운 또 다른 접근법이 있습니다.
serialize_variables () {
for __serialize_variables_var do
eval "__serialize_variables_tail=\${$__serialize_variables_var}"
while __serialize_variables_quoted="$__serialize_variables_quoted${__serialize_variables_tail%%\'*}"
[ "${__serialize_variables_tail%%\'*}" != "$__serialize_variables_tail" ]; do
__serialize_variables_tail="${__serialize_variables_tail#*\'}"
__serialize_variables_quoted="${__serialize_variables_quoted}'\\''"
done
printf "$__serialize_variables_var='%s'\n" "$__serialize_variables_quoted"
done
}
읽기 전용 변수를 허용하는 쉘에서는 읽기 전용 변수를 다시 읽으려고하면 오류가 발생합니다.
$PWD
하고 $_
- 아래에 자신의 의견을 참조하시기 바랍니다.
typeset
대한 별칭을 만드는 방법은 typeset -g
무엇입니까?
많은 내 이전의 시도와 모든 문제를 지적 @ 스테판 - Chazelas가 덕분에이 이제 표준 출력 또는 변수로 배열에는 직렬화하기 위해 작동하는 것 같다.
이 기술은 declare -a
/ 와 달리 입력을 셸 구문 분석하지 않으므로 declare -p
직렬화 된 텍스트에서 메타 문자가 악의적으로 삽입되는 것을 방지합니다.
참고 : 문자 쌍을 read
삭제 하기 때문에 줄 바꿈이 이스케이프되지 않으므로 대신 읽기 위해 전달 된 다음 이스케이프되지 않은 줄 바꿈이 유지됩니다.\<newlines>
-d ...
이 모든 unserialise
기능 에서 관리됩니다 .
필드 분리 자와 레코드 분리기의 두 가지 마법 문자가 사용되므로 여러 배열을 동일한 스트림으로 직렬화 할 수 있습니다.
이 문자는 다음과 같이 정의 할 수 없습니다 FS
하고 RS
있지만, 어느 쪽도 정의 할 수 있습니다 newline
이스케이프 줄 바꿈에 의해 삭제 되었기 때문에 문자 read
.
이스케이프 문자는 문자로 인식되지 않도록하기 위해 \
사용되는 백 슬래시 여야합니다 .read
IFS
serialise
에는 직렬화됩니다 "$@"
표준 출력에, serialise_to
에 명명 된 varable에에는 직렬화합니다$1
serialise() {
set -- "${@//\\/\\\\}" # \
set -- "${@//${FS:-;}/\\${FS:-;}}" # ; - our field separator
set -- "${@//${RS:-:}/\\${RS:-:}}" # ; - our record separator
local IFS="${FS:-;}"
printf ${SERIALIZE_TARGET:+-v"$SERIALIZE_TARGET"} "%s" "$*${RS:-:}"
}
serialise_to() {
SERIALIZE_TARGET="$1" serialise "${@:2}"
}
unserialise() {
local IFS="${FS:-;}"
if test -n "$2"
then read -d "${RS:-:}" -a "$1" <<<"${*:2}"
else read -d "${RS:-:}" -a "$1"
fi
}
다음과 같이 직렬화 해제하십시오.
unserialise data # read from stdin
또는
unserialise data "$serialised_data" # from args
예 :
$ serialise "Now is the time" "For all good men" "To drink \$drink" "At the \`party\`" $'Party\tParty\tParty'
Now is the time;For all good men;To drink $drink;At the `party`;Party Party Party:
(후행 줄 바꿈없이)
다시 읽어보십시오.
$ serialise_to s "Now is the time" "For all good men" "To drink \$drink" "At the \`party\`" $'Party\tParty\tParty'
$ unserialise array "$s"
$ echo "${array[@]/#/$'\n'}"
Now is the time
For all good men
To drink $drink
At the `party`
Party Party Party
또는
unserialise array # read from stdin
Bash read
는 이스케이프 문자 \
(-r 플래그를 전달하지 않은 경우)를 존중하여 입력 필드 구분 또는 행 구분과 같은 문자의 특수 의미를 제거합니다.
단순한 인수 목록 대신 배열을 직렬화하려면 배열을 인수 목록으로 전달하십시오.
serialise_array "${my_array[@]}"
랩핑 된 읽기이기 때문에 unserialise
루프에서 사용할 수 read
있지만 스트림은 줄 바꿈으로 구분되지 않습니다.
while unserialise array
do ...
done
bash
있고로 zsh
렌더링하면 작동하지 않습니다 $'\xxx'
. bash -c $'printf "%q\n" "\t"'
또는bash -c $'printf "%q\n" "\u0378"'
$IFS
수정되지 않은 상태 에 따라 달라지며 이제 빈 배열 요소를 올바르게 복원하지 못합니다. 실제로, 다른 IFS 값을 사용하고 -d ''
개행을 피할 필요가 없도록 하는 것이 더 합리적 입니다. 예를 들어 :
필드 구분 기호로 사용 하고 해당 백 슬래시를 이스케이프 처리하고 IFS=: read -ad '' array
가져 오기만 사용하십시오 .
read
. backslash-newline for read
는 논리적 행을 다른 실제 행으로 계속하는 방법입니다. 편집 : 아 이미 줄 바꿈 문제가 언급되어 있습니다.
printf 'VAR=$(cat <<\'$$VAR$$'\n%s\n'$$VAR$$'\n)' "$VAR" >./VAR.file
이를 수행하는 또 다른 방법은 '
다음과 같이 모든 따옴표 를 처리하는 것입니다.
sed '"s/'"'/&"&"&/g;H;1h;$!d;g;'"s/.*/VAR='&'/" <<$$VAR$$ >./VAR.file
$VAR
$$VAR$$
또는과 export
:
env - "VAR=$VAR" sh -c 'export -p' >./VAR.file
첫 번째 옵션과 두 번째 옵션은 변수 값에 문자열이 포함되어 있지 않다고 가정하면 POSIX 셸에서 작동합니다.
"\n${CURRENT_SHELLS_PID}VAR${CURRENT_SHELLS_PID}\n"
세 번째 옵션은 POSIX 쉘에 대한 작업을해야하지만 같은 다른 변수를 정의하기 위해 시도 할 수 _
또는 PWD
. 진실은, 그것이 정의하려고 시도 할 수있는 유일한 변수는 쉘 자체에 의해 설정되고 유지되므로-만약 당신이 export
그들 중 하나에 대한 값을 가져 오더라도 -예 $PWD
를 들어-쉘은 단순히 그것들을 어쨌든 올바른 가치-즉시 노력하고 PWD=any_value
직접보십시오.
그리고 적어도 GNU의 bash
디버그 출력은 쉘에 다시 입력하기 위해 자동으로 안전하게 인용되기 때문에 다음의 '
하드 따옴표 수에 관계없이 작동 합니다 "$VAR"
.
PS4= VAR=$VAR sh -cx 'VAR=$VAR' 2>./VAR.file
$VAR
다음 경로가 유효한 스크립트에서 나중에 저장된 값으로 설정할 수 있습니다.
. ./VAR.file
$$
실행중인 쉘의 PID \$
입니까 , 따옴표가 잘못 되었거나 의미가 있습니까? here 문서를 사용하는 기본 접근 방식은 작동하도록 만들 수 있지만 하나의 라이너가 아닌 까다로운 작업입니다. 끝 마커로 선택하면 문자열에 나타나지 않는 것을 선택해야합니다.
$VAR
포함 하면 두 번째 명령이 작동하지 않습니다 %
. 세 번째 명령은 여러 줄을 포함하는 값 (항상 누락 된 큰 따옴표를 추가 한 후에도)에서 항상 작동하지는 않습니다.
env
. 나는 여전히 여러 줄에 대해 당신이 무엇을 의미하는지 궁금 합니다. 마지막 sed
때까지 만날 VAR=
때까지 모든 줄을 삭제 하십시오. 그래서 모든 줄 $VAR
이 전달됩니다. 그것을 깨는 예를 제공해 줄 수 있습니까?
VAR
)이 변경되지 않았 PWD
거나 _
일부 쉘이 정의하는 다른 이름이라고 가정합니다 . 두 번째 방법은 bash가 필요합니다. 의 출력 형식 -v
이 표준화되지 않았습니다 (대시, ksh93, mksh 및 zsh는 작동하지 않음).
거의 동일하지만 약간 다릅니다.
스크립트에서 :
#!/usr/bin/ksh
save_var()
{
(for ITEM in $*
do
LVALUE='${'${ITEM}'}'
eval RVALUE="$LVALUE"
echo "$ITEM=\"$RVALUE\""
done) >> $cfg_file
}
restore_vars()
{
. $cfg_file
}
cfg_file=config_file
MY_VAR1="Test value 1"
MY_VAR2="Test
value 2"
save_var MY_VAR1 MY_VAR2
MY_VAR1=""
MY_VAR2=""
restore_vars
echo "$MY_VAR1"
echo "$MY_VAR2"
이번에는 위의 테스트를 거쳤습니다.
'
, *
등
echo "$LVALUE=\"$RVALUE\""
줄 바꿈도 유지해야하며 cfg_file의 결과는 다음과 같아야합니다. 저장된 값에 "
char이 포함 된 경우 물론 문제가있을 수 있습니다 . 그러나 그것은 또한주의를 기울일 수 있습니다.