다른 스크립트의 내용으로 사용하기 위해 변수를 이스케이프하세요

다른 스크립트의 내용으로 사용하기 위해 변수를 이스케이프하세요

질문은 ~이야아니요올바르게 이스케이프된 문자열 리터럴을 작성하는 방법에 대해 설명합니다. 스크립트나 다른 프로그램에서 직접 사용하기 위해 변수를 이스케이프하는 방법과 관련되지 않은 질문을 찾을 수 없습니다.

내 목표는 하나의 스크립트가 다른 스크립트를 생성할 수 있도록 만드는 것입니다. 이는 생성된 스크립트의 작업 범위가 0에서N다른 컴퓨터에서 여러 번 실행하면 이를 생성하는 데이터가 다시 실행되기 전에 변경될 수 있으므로 네트워크를 통해 직접 수행하는 것은 작동하지 않습니다.

특수 문자(예: 작은따옴표)를 포함할 수 있는 알려진 변수가 있는 경우 이를 완전히 이스케이프된 문자열 리터럴로 작성해야 합니다. 예를 들어 foo포함된 변수는 bar'baz생성된 스크립트에 다음과 같이 나타나야 합니다.

qux='bar'\''baz'

이는 "qux=$foo_esc"스크립트에 추가 행을 추가하여 작성됩니다. 저는 Perl을 사용하여 다음과 같이 했습니다.

foo_esc="'`perl -pe 's/('\'')/\\1\\\\\\1\\1/g' <<<"$foo"`'"

하지만 이건 너무 과한 것 같습니다.

나는 성공하지 못한 채 bash를 단독으로 사용했습니다. 나는 다음과 같은 다양한 변형을 시도했습니다.

foo_esc="'${file//\'/\'\\\'\'}'"
foo_esc="'${file//\'/'\\''}'"

그러나 추가 슬래시가 출력에 나타나거나(이 작업을 수행할 때 echo "$foo") 구문 오류가 발생합니다(셸에서 수행한 경우 추가 입력이 예상됨).

답변1

Bash에는 이러한 상황에 대한 매개변수 확장 옵션이 있습니다.:

${parameter@Q}확장자는 값이 다음과 같은 문자열입니다.범위입력으로 재사용할 수 있는 형식으로 인용하세요.

따라서 이 경우:

foo_esc="${foo@Q}"

Bash 4.4 이상에서는 이 기능을 지원합니다. 다른 형태의 확장, 특히 완전한 할당문 생성을 위한 다양한 옵션이 있습니다 () @A.

답변2

Bash는 Bash의 이전 버전(<4.0)에서도 쉘 이스케이프를 수행할 수 있는 형식 지정자 printf와 함께 내장 함수를 제공합니다.%q

printf '[%q]\n' "Ne'er do well"
# Prints [Ne\'er\ do\ well]

printf '[%q]\n' 'Sneaky injection $( whoami ) `ls /root`'
# Prints [Sneaky\ injection\ \$\(\ whoami\ \)\ \`ls\ /root\`]

이 트릭은 함수에서 데이터 배열을 반환하는 데에도 사용할 수 있습니다.

function getData()
{
  printf '%q ' "He'll say hi" 'or `whoami`' 'and then $( byebye )'
}

declare -a DATA="( $( getData ) )"
printf 'DATA: [%q]\n' "${DATA[@]}"
# Prints:
# DATA: [He\'ll\ say\ hi]
# DATA: [or\ \`whoami\`]
# DATA: [and\ then\ \$\(\ byebye\ \)]

Bash 내장 명령은 printf대부분의 Unix 계열 운영 체제에 번들로 제공되는 유틸리티와 다릅니다. printf어떤 이유로든 printf명령이 내장 명령 대신 유틸리티를 호출하는 경우 언제든지 실행할 수 있습니다 builtin printf.

답변3

요약: 결론으로 ​​건너뛰세요.

여러 쉘/도구에는 따옴표 연산자가 내장되어 있지만 그 중 일부는 일부 답변에서 언급되었지만 여기서는 강조하고 싶습니다.많은 것들이 사용하기에 안전하지 않습니다에 따르면:

  • 인용된 내용
  • 인용된 문자열이 사용되는 컨텍스트입니다.
  • 인용된 출력을 생성하는 로케일
  • 결과로 인용된 출력은 나중에 해당 로케일에 사용됩니다.

고려해야 할 몇 가지 사항:

  • 어떤 경우에는 ''빈 문자열을 또는 로 표현하는 것이 중요합니다 "". 예를 들어 에서 사용하려는 경우 sh -c "cmd $quoted_output"참조 내용을 에 인수로 전달해도 상관 없습니다 cmd.sh -c "var=$quoted_output; ..."''""

    $var:q의 연산자 는 빈 문자열을 , 도 zsh아닌 빈 문자열로 나타냅니다 .''""$''

    연산자 (자체 복사됨, 이와 관련하여 다르게 ${var@Q}동작함 )는 빈 문자열 을 로 나타내지 만 빈 문자열로 설정되지 않음을 나타냅니다.bashmksh$var''$var

    $ empty_var= bash -c 'printf "<%s>\n" "${empty_var@Q}" "${unset_var@Q}"'
    <''>
    <>
    $ empty_var= mksh -c 'printf "<%s>\n" "${empty_var@Q}" "${unset_var@Q}"'
    <''>
    <''>
    $ empty_var= zsh -c 'printf "<%s>\n" "${empty_var:q}" "${unset_var:q}"'
    <>
    <>
    
  • '...'\이러한 참조 연산자 중 일부는 , "..."또는 의 조합을 사용합니다 $'...'. 후자의 구문은 쉘과 해당 쉘의 버전에 따라 다릅니다. 따라서 이를 사용하거나 입력에 따라 사용할 수 있는 연산자의 경우 동일한 셸(및 동일한 버전)에서 결과를 사용하는 것이 중요합니다. 이는 최소한 다음에 적용됩니다.

    • printf %qGNU printf, bash, ksh93,zsh
    • zsh의 ,,,,$var:q${(q)var}${(q+)var}${(qqqq)var}
    • mksh~의${var@Q}
    • bash${var@Q},
    • typeset// declare, , , 의 출력 export -p(이전 버전의 스칼라 변수에는 적용되지 않음)ksh93mkshzshbash
    • alias/ set출력 bash, ksh93, mksh,zsh
    • xtrace출력 ksh93, mksh,zsh

    그럼에도 불구하고 $'...'이는 (아직은) 표준 sh참조 연산자이며 Bourne과 유사한 쉘이 아닌 , rc, esakanga이미 fish완전히 포함되어 있습니다.다른 인용 구문. 존재하는 모든 쉘과 호환되는 방식으로 문자열을 인용할 수 있는 방법은 없습니다.또 다른 Q&A입니다몇 가지 해결 방법).

  • 일부 쉘은 내부 코드를 해석하기 전에 입력을 문자로 디코딩하고 일부는 그렇지 않으며 일부는 때때로 이 작업을 수행하고 때로는 그렇지 않습니다.

    일부 쉘(예: bash)은 해당 구문을 로케일에 따라 조건부로 만듭니다. 예를 들어 구문의 토큰 구분 기호는 yash및 로케일에서 공백으로 처리되는 문자 입니다 bash(단 bash, 에서는 단일 바이트 문자에만 적용됨). 일부 쉘은 또한 로케일의 문자 분류에 의존하여 변수 이름에 유효한 문자를 결정합니다. 예를 들어, Stéphane=1한 로케일에서는 할당으로 해석되거나 Stéphane=1다른 로케일에서는 명령 호출로 해석될 수 있습니다.

    바이트 시퀀스 0xa3 0x5c는 £\ISO-8859-1(latin1이라고도 함) 문자 집합의 문자열, αBIG5의 문자 또는 UTF-8의 잘못된 바이트 시퀀스를 나타냅니다. inside 및 를 \포함하여 쉘 구문의 특수 문자입니다 . 또한 일부 로케일의 다른 문자 인코딩에서 인코딩을 찾을 수 있는 (위험한) 문자입니다."..."$'...'`

    byte는 0xa0다음과 같이 처리되는 대형 단일 바이트 문자 세트의 줄바꿈 ​​없는 공백 문자입니다.공백bash또는 구문 yash의 토큰 구분 기호 와 같은 일부 시스템의 일부 로케일에서 .

    이 바이트는 많은 알파벳 문자를 포함하여 수천 개의 문자를 UTF-8로 인코딩하는 경우에도 발견됩니다(예: à0xc3 0xa0으로 인코딩됨).

    이 인코딩을 포함하는 문자에 대한 인코딩이 있는 ASCII 기반 시스템의 모든 로케일에서 사용되는 문자 세트를 인식하지 못합니다 '.

    예를 들어, 일부 셸 인용 연산자 출력 $'\u00e9'또는 $'\u[e9]'문자입니다. é차례로, 사용하는 경우 쉘 및 로케일에 따라 이를 사용하는 코드가 해석되거나 실행될 때 UTF-8 인코딩 또는 로케일 인코딩으로 확장됩니다(로케일이 다르면 동작이 달라집니다). 이 캐릭터).

    따라서 생성된 문자열이 동일한 쉘 및 쉘 버전뿐만 아니라 동일한 로케일에서도 사용되는 것이 중요합니다(적어도 일부 문자 인코딩/디코딩을 수행하는 쉘의 경우). 그럼에도 불구하고 일부 쉘( 포함 bash)에는 이와 관련하여 여전히 버그가 있거나 있었습니다.

    $'...'"..."인용을 위해 , 백슬래시를 사용하거나 ASCII가 아닌 특정 문자를 인용되지 않은 채로 두는 인용 연산자는 안전하지 않을 수 있습니다 .

    즉, '...'이 점에서는 그것을 사용하는 사람만이 안전합니다. 유지하다:

    • zsh운영자${(qq)var}
    • / alias(적어도 현재 버전).dashbashbosh
    • export -p/ dash( bosh적어도 현재 버전).
    • (적어도 현재 버전) set.dash

    이들 중 첫 번째 항목만 문서화되어 있으며 항상 작은따옴표를 사용할 것을 약속합니다(아래 경고에 유의하세요 rcquotes).

    또한 yash로캘의 문자 집합에서 디코딩할 수 없는 데이터는 처리할 수 없으므로 임의의 데이터를 이 셸에 전달할 수 없습니다(적어도 현재 버전에서는).

    아이러니하게도 유틸리티의 출력에 문제가 있습니다( 출력하는 데 locale사용해야 하기 때문에)."..."암묵적으로locale설정), 호출된 위치와 다른 로케일에 코드를 입력하는 데 자주 사용됩니다 (로케일을 복원하기 위해).

  • NUL 문자(0바이트)는 환경 변수 또는 execve()시스템 호출을 통해 실행되는 명령에 대한 인수에 나타날 수 없습니다. 이는 이러한 env 및 인수 문자열을 C 스타일 NUL로 구분된 문자열로 처리하는 해당 시스템 호출의 제한 사항입니다. ) ). 내부를 제외하고 zshNUL은 쉘 변수나 내장 매개변수 또는 보다 일반적인 쉘 코드에서는 찾을 수 없습니다.

    그러나 0바이트는 가능합니다.읽다그리고파일이나 파이프 또는 모든 I/O 메커니즘과 주고받을 수 있습니다.

    현대 프로그래밍 언어(예: 또는 ) 와 마찬가지로 변수에 저장하고 읽고 쓸 수 zsh있으며 내장 함수에 인수로 전달할 수 있습니다 .pythonperl

    그러나 NUL을 있는 그대로 두는 방법(예 $'\0': $'\x0', $'\u0000', $'\C@'제외)을 사용하여 NUL을 참조하는 경우 참조 방법에 관계없이 결과를 매개변수 또는 환경 변수로 전달할 수 없습니다.처형된명령이며 NUL 문자는 다른 쉘에서 사용할 수 없습니다.

    zsh(as in) 에서 외부 입력을 허용하는 경우 이 점을 염두에 두는 것이 좋습니다 IFS= read -r var. stdin에서 읽은 행에 NUL 바이트가 포함되어 있으면 이를 포함하여 수행할 수 있는 작업이 제한될 수 있습니다 $var.${(qq)var}

    이 경우에는 $'...'참조 양식을 사용하는 것이 더 나을 수 있습니다(해당 참조 양식(위 참조)과 관련된 다른 고려 사항을 해결할 수 있는 경우).

  • 생성된 인용 텍스트가 백틱 내의 쉘 코드에서 사용되는 경우 백슬래시 해석의 추가 계층이 있다는 점에 유의하십시오. 항상 $(...)대신 사용하십시오 `...`.

  • 일부 문자는 특정 상황에서만 특별합니다. 예를 들어, =명령 이름 앞의 단어는 특별하지만(예: a=1 cmd arg), 명령 이름 뒤의 단어는 특별하지 않습니다(예: ). 그러나 일부 쉘에는 cmd a=1...와 같은 명령에 대한 몇 가지 특별한 경우가 있습니다.exportreadonly

    ~어떤 경우에는 특별하지만 다른 경우에는 그렇지 않습니다.

    모든 참조 연산자가 이를 참조하는 것은 아닙니다.

    일부 문자는 일부 쉘에서는 특별하지만 다른 쉘에서는 특별하지 않거나 특정 옵션이 활성화된 경우에만...

    짝수는 어떤 경우에는 특별합니다. 예를 들어 인용문이 없으면 sh -c "echo ${quoted_text}>file"인용된 텍스트는 출력되지 않습니다.file2'2'

  • 에서 zshrcquotes옵션은 작은따옴표로 묶인 문자열이 해석되는 방식(및 인용 연산자에 의해 생성되는 방식)에 영향을 줍니다. 활성화되면 작은따옴표가 ''셸에서 작은따옴표 문자열로 표시될 수 있습니다 rc. 예를 들어 로 "foo'bar"쓸 수도 있습니다 'foo''bar'.

    rcquotes따라서 활성화되었을 때 생성된 인용 문자열은 zsh활성화된 인스턴스에서만 해석 될 수 있다는 것이 중요합니다 rcquotes.

    ${(qq)var}zsh가 있거나 없는 빌드는 rcquotes에서 사용하는 것이 안전 해야 합니다. 하지만 작은따옴표로 묶인 문자열을 연결하면 그 사이에 작은따옴표가 삽입된다는 점 zsh -o rcquotes에 유의하세요 .zsh -o rcquotes

    $ quoted_text="'*'"
    $ zsh -o rcquotes -c "echo $quoted_text$quoted_text"
    *'*
    

    그것은 다음과 같습니다:

    $ rc -c "echo $quoted_text$quoted_text"
    *'*
    

    ""다음 둘 사이에 삽입하여 이 문제를 해결할 수 있습니다.

    $ zsh -o rcquotes -c "echo $quoted_text\"\"$quoted_text"
    **
    

    in rc및 파생어( "..."참조 연산자가 아니고 유일한 인용 유형이므로 삽입 '...'할 수 있어야 함 )에서는 다음을 사용할 수 있습니다 .'^

    $ rc -c "echo $quoted_text^$quoted_text"
    **
    

요약하자면

인용하는 유일한 안전한 방법은(Bourne과 같은 쉘로 제한되고 악성 로케일을 무시하거나 yash데이터 `...`에 NUL 문자가 포함되어 있지 않다고 가정하는 경우) 모든 것을 작은따옴표로 묶는 것입니다(빈 문자열도 포함). 당신은 문자를 원합니다) 그것이 결코 문제가 되지 않을 것이라고 상상하고 질문의 원래 의도와 같이 작은 따옴표 문자 자체를 작은 따옴표 또는 작은 따옴표 \'외부로 나타냅니다."'"

이렇게 하려면 다음을 사용할 수 있습니다.

  • zsh연산자 ${(qq)var}(또는 "${(qq@)array}"배열의 경우), 이 rcquotes옵션이 활성화되어 있지 않다고 가정합니다.

  • 다음과 같은 함수:

    shquote() {
      LC_ALL=C awk -v q="'" '
        BEGIN{
          for (i=1; i<ARGC; i++) {
            gsub(q, q "\\" q q, ARGV[i])
            printf "%s ", q ARGV[i] q
          }
          print ""
        }' "$@"
    }
    

    또는

    shquote() {
      perl -le "print join ' ', map {q(') . s/'/'\\\\''/gr . q(')} @ARGV" -- "$@"
    }
    
  • ksh93/// zsh:bashmksh

    quoted_text=\'${1//\'/\'\\\'\'}\'
    

    (확장을 큰따옴표로 묶지 말고 스칼라 변수 할당 외부에서 사용하지 마십시오. 그렇지 않으면 버전 간 호환성 문제가 발생합니다 bash(옵션 설명 참조 compat41).


^POSIX 사양$'...'원래 목표는 단일 UNIX 사양 8호였으며 이르면 2021년에 출시될 것으로 예상되지만 그렇게 되지는 않을 것 같습니다(제때 솔루션에 대한 합의가 이루어지지 않았습니다). 따라서 $'...'표준에 추가되기 까지 적어도 10년은 더 기다려야 할 것입니다.

² Bourne 쉘 및 일부 파생 제품의 -k( ) 옵션이 활성화되지 않은 경우keyword

답변4

var 값을 참조하는 방법에는 여러 가지가 있습니다.

  1. 별칭
    별칭을 사용할 수 있는 대부분의 쉘에서(csh, tcsh 및 기타 csh 유사 쉘 제외):

    $ alias qux=bar\'baz
    $ alias qux
    qux='bar'\''baz'
    

    예, 이는 sh대시(Dash) 또는 재(Ash)와 같은 많은 유사한 쉘에서 작동합니다.

  2. set은
    대부분의 쉘에서도 작동합니다(csh는 아님).

    $ qux=bar\'baz
    $ set | grep '^qux='
    qux='bar'\''baz'
    
  3. 일부 셸
    (적어도 ksh, bash 및 zsh)에서는 다음을 수행합니다.

    $ qux=bar\'baz
    $ typeset -p qux
    typeset qux='bar'\''baz'             # this is zsh, quoting style may
                                         # be different for other shells.
    
  4. 내보내기
    먼저 수행할 작업:

    export qux=bar\'baz
    

    그런 다음 다음을 사용하십시오.
    export -p | grep 'qux=' export -p | grep 'qux='
    export -p qux

  5. 인용하다
    echo "${qux@Q}"
    echo "${(qq)qux}" # 1~4개의 q를 사용할 수 있습니다.

관련 정보