밝히다

밝히다

이 질문은 복잡해 보일 수도 있지만 그렇지 않습니다! 고려하다:

% 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 @@bashargv

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' 

위의 내용은 이러한 쉘에 내장된 기능과 함께 작동하는 방식입니다. 이 외에도 printfcoreutils의 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"는 나머지 인수와 함께 형식 문자열을 "반복"하여 작동합니다. 따라서 이러한 예에서는 끝에 후행 공백이 있습니다.

관련 정보