이 질문은 복잡해 보일 수도 있지만 그렇지 않습니다! 고려하다:
% f() { echo "$@"; }
% f a
a
% f cmd -o"value with space"
cmd -ovalue with space
% f cmd -ovalue with space
cmd -ovalue with space
% f cmd -o'value with "quotes"'
cmd -ovalue with "quotes"
% f cmd -ovalue with "quotes"
cmd -ovalue with quotes
분명히 "간격 값" 속성은 손실되는 매개변수일 뿐이며, 마찬가지로 다시 입력하면 큰따옴표가 "먹혀집니다".
원하는 출력은 동일한 출력을 생성하기 위해 입력으로 다시 사용할 수 있는 출력입니다.
BASH에는 이를 가능하게 하는 기능이 내장되어 있지 않은 것 같습니다. 그렇죠?
밝히다
수행하려는 작업이 확실하지 않은 경우: 쉘 배열에 명령이 저장되어 있고 사용자가 출력을 복사하여 쉘 프롬프트(또는 스크립트)에 붙여넣을 수 있도록 이러한 배열을 표준 출력으로 인쇄하고 싶습니다. 배열에서 동일한 명령을 재현하기 위해.
다음 (어리석은) 예를 생각해 보세요.
> X=(echo "Bob's car is named \"Bobby\"")
정상적인 것이 echo "${X[@]}"
출력됩니다
echo Bob의 차 이름은 "Bobby"입니다.
하지만하나가능한 올바른 출력은 다음과 같습니다.
echo Bob의 차 이름은 "Bobby"입니다.
답변1
해당 기간 동안 변신 옵션 중 하나Bash의 매개변수 확장예(Bash 4.4부터 사용 가능한 것으로 보이며 이전 버전에서는 "잘못된 대체" 출력):
${parameter@operator}
확장은 가치의 변화이거나범위또는 관련 정보범위그 자체는 연산자의 값에 따라 달라집니다. 각운영자편지입니다:[...]
Q
확장자는 값이 다음과 같은 문자열입니다.범위입력으로 재사용할 수 있는 형식으로 인용하세요.
bash-5.2$ f() { echo "${@@Q}"; }
bash-5.2$ f cmd -o'value with "quotes"'
'cmd' '-ovalue with "quotes"'
bash-5.2$ f cmd -ovalue with "quotes"
'cmd' '-ovalue' 'with' 'quotes'
답변2
당신이 찾고있는 기능은 다음과 같습니다직렬화(직렬화미국 영어).
여기서 간단한 명령은 하나 이상의 문자열 배열이므로 결국 배열 직렬화로 요약됩니다.
execve()
명령이 외부 명령인 경우 NUL 바이트는 C 스타일 문자열로 시스템 호출에 전달되므로 문자열에 NUL 바이트를 포함할 수 없다는 추가 제한 사항이 있습니다 . 대부분의 셸에서는 execve()
시스템 호출을 포함하지 않는 명령(예: 내장 명령 또는 함수)에도 동일한 제한 사항이 적용됩니다. 유일한 예외는 zsh 셸입니다.
따라서 명령 인수에 NUL 바이트가 포함되어 있지 않다고 가정할 수 있다면 직렬화는 쉽습니다. NUL로 구분하여 인쇄하기만 하면 됩니다.
print0() {
[ "$#" -eq 0 ] ||
printf '%s\0' "$@"
}
print0 cmd -o"value with space" > file
4.4 이상 에서는 bash
이를 매개변수 목록으로 다시 읽는 것은 다음과 같습니다.
readarray -td '' args < file
어디-d ''
NUL 바이트를 구분 기호로 설정, -t
변수에 NUL을 저장할 수 없는 현재 버전의 bash에서는 반드시 필요하지 않은 값에서 구분 기호를 제거합니다.
그런 다음 다음을 수행하십시오.
"${args[@]}"
명령을 실행합니다.
아니면 GNU를 사용해도 됩니다 xargs
:
xargs -r0a file env
그러나 zsh를 제외한 다른 모든 쉘에서처럼 이 직렬화의 결과를 변수에 저장할 수 없으며 쉘 변수의 값은 NUL 바이트를 가질 수 없습니다.
JSON, XML, YAML은 복잡한 데이터 구조를 직렬화하기 위한 일반적인 형식이지만 자체적인 문제도 있습니다(예: JSON 문자열은 문자로 구성되어야 하지만 매개변수 문자열은 임의 바이트의 배열임). 더 중요한 것은 쉘이 거의 없다는 것입니다. 구문 분석에 대한 지원이 내장되어 있습니다(ksh93v-beta 버전에는 JSON 구문 분석에 대한 일부 실험적 지원이 있었지만 버그가 있어 최신 버전에서는 제거되었습니다).
일부 언어에는 직렬화 형식이 내장되어 있습니다. 예를 php
들어 ,serialize()
그리고 그에 상응하는unserialize()
기능적이지만 php
명령을 실행하는 데 적합한 API가 없습니다.
해석된 언어의 일반적인 접근 방식은 코드로 직렬화하는 것입니다. 예를 들어, 이것이 Data::Dumper
하는 일입니다. 매개변수로 및 매개변수가 있는 배열이 있는 경우 perl
이를 저장한 다음 Perl 코드를 평가하여 배열을 다시 가져올 수 있습니다.cmd
-ovalue with space
@array = ("cmd", "-o value with space")
bash 또는 zsh와 같은 Korn과 유사한 쉘에서는 이것이 정확히 수행되기 때문에 수행하기 쉽습니다 typeset -p
. zsh에서는 함수 typeset -p argv
에서 이 작업을 수행 할 수 있지만 변수가 아니기 때문에 serialise
그렇지 않습니다 . 에서는 위치 매개변수가 변수에 매핑되지 않지만 여전히 임시 배열을 사용할 수 있습니다.typeset -p @
@
bash
argv
serialise() {
local args
args=( "$@" )
typeset -p args
}
serialised=$(serialise cmd -o"value with space")
그런 다음 역직렬화는 다음과 같습니다.
eval "$serialised"
그러면 배열이 생성됩니다 $args
(함수 내에서 실행되는 경우 배열은 해당 함수에 대해 로컬이 됩니다).
그런 다음:
"${args[@]}"
명령을 다시 실행하십시오.
직렬화 해제는 직렬화를 수행할 때와 동일한 셸 버전, 동일한 운영 체제 및 동일한 로케일을 사용하여 수행되어야 합니다. 바라보다"다른 스크립트의 내용으로 사용하기 위해 변수를 탈출하세요"에 대한 답변입니다.문자열을 직렬화하는 방법에 대한 자세한 내용.
완전성을 기하기 위해 ksh93은 다차원 배열, 구조 및 객체를 포함하여 다른 셸보다 더 복잡한 데이터 구조를 가지므로 직렬화 및 역직렬화 지원 기능이 내장되어 있습니다.
- 직렬화:
print -C var
- 역직렬화:
read -C var
예를 들어 다음을 사용하여 변수를 복사할 수 있습니다.
print -C var | read -C var_copy
답변3
다른 답변에 추가하려면 bash (및 zsh)에는 printf
다음이 있습니다.%q
%q는 printf가 해당 인수를 쉘 입력으로 재사용할 수 있는 형식으로 출력하도록 합니다.
$ f() { printf '%q ' "$@"; echo; }
$ f cmd -o"value with space" -o'"'\' -o"'" -o"'\\" -o'\' $'\n'
cmd -ovalue\ with\ space -o\"\' -o\' -o\'\\ -o\\ $'\n'
위의 내용은 이러한 쉘에 내장된 기능과 함께 작동하는 방식입니다. 이 외에도 printf
coreutils의 GNU도 이를 지원하지만 출력은 이스케이프되지 않고 인용됩니다.
$ f() { /bin/printf '%q ' "$@"; echo; }
$ f cmd -o"value with space" -o'"'\' -o"'" -o"'\\" -o'\' $'\n'
cmd '-ovalue with space' '-o"'\''' "-o'" '-o'\''\' '-o\' ''$'\n'
"printf"는 나머지 인수와 함께 형식 문자열을 "반복"하여 작동합니다. 따라서 이러한 예에서는 끝에 후행 공백이 있습니다.