작은 따옴표로 묶은 문자열 내에서 작은 따옴표를 이스케이프 처리하는 방법


1016

Bash가 있다고 가정 해 보겠습니다 alias.

alias rxvt='urxvt'

잘 작동합니다.

하나:

alias rxvt='urxvt -fg '#111111' -bg '#111111''

작동하지 않으며 둘 다 작동하지 않습니다.

alias rxvt='urxvt -fg \'#111111\' -bg \'#111111\''

따옴표를 이스케이프하면 문자열 내에서 여는 따옴표와 닫는 따옴표를 어떻게 일치 시키는가?

alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''

그렇게 연결하면 허용되는 경우 동일한 문자열을 나타내더라도 좋지 않은 것처럼 보입니다.


16
별칭에 작은 따옴표를 사용할 필요가 없다는 것을 알고 있습니까? 큰 따옴표가 훨씬 쉽습니다.
teknopaul


3
중첩 된 큰 따옴표는 이스케이프 가능 "\""하므로 가능한 경우 @liori의 답변보다 우선적으로 사용해야합니다.
alan

7
큰 따옴표는 * nix (Bash 및 Perl과 같은 관련 도구 포함)의 작은 따옴표와는 다르게 작동하므로 작은 따옴표에 문제가있을 때마다 큰 따옴표를 대체하는 것은 좋은 해결책이 아닙니다. 큰 따옴표는 $ ...를 지정하고 변수는 실행 전에 대체되고 작은 따옴표는 $ ...를 문자 적으로 처리합니다.
Chuck Kollars 19

생각하고 있다면 큰 따옴표를 사용했지만 여전히 작동하지 않습니다 . 스크립트를 다시 소스하십시오.
Samy Bencherif

답변:


1453

가장 바깥층에 작은 따옴표를 사용하려면 두 종류의 따옴표를 붙일 수 있습니다. 예:

 alias rxvt='urxvt -fg '"'"'#111111'"'"' -bg '"'"'#111111'"'"
 #                     ^^^^^       ^^^^^     ^^^^^       ^^^^
 #                     12345       12345     12345       1234

다음 '"'"'과 같이 해석되는 방법 에 대한 설명 ':

  1. ' 작은 따옴표를 사용하는 첫 번째 따옴표를 종료하십시오.
  2. " 큰 따옴표를 사용하여 두 번째 견적을 시작하십시오.
  3. ' 인용 문자.
  4. " 큰 따옴표를 사용하여 두 번째 견적을 종료하십시오.
  5. ' 작은 따옴표를 사용하여 세 번째 견적을 시작하십시오.

(1)과 (2) 또는 (4)와 (5) 사이에 공백을 두지 않으면 쉘은 해당 문자열을 하나의 긴 단어로 해석합니다.


5
alias splitpath='echo $PATH | awk -F : '"'"'{print "PATH is set to"} {for (i=1;i<=NF;i++) {print "["i"]",$i}}'"'"별칭 문자열에 작은 따옴표와 큰 따옴표가 모두있을 때 작동합니다!
오르막 _ What'1

17
내 해석 : bash는 다르게 인용 된 문자열 표현식을 암시 적으로 연결합니다.
Benjamin Atkin

2
이중 이스케이프 된 작은 따옴표의 예를 들어 나를 위해 일했습니다 :alias serve_this_dir='ruby -rrack -e "include Rack;Handler::Thin.run Builder.new{run Directory.new'"'"''"'"'}"'
JAMESSTONEco

2
가장 읽기 쉬운 솔루션은 아닙니다. 실제로 필요하지 않은 작은 따옴표를 과도하게 사용합니다.
14:38에

26
나는 그것이 '\''대부분의 상황에서보다 훨씬 더 읽기 쉽다고 주장한다 '"'"'. 실제로 전자는 작은 따옴표로 묶인 문자열 내에서 거의 항상 명확하게 구분되므로 큰 따옴표로 묶인 문자열에서와 같이 의미가 "이스케이프 된 따옴표입니다"의미로 의미 적으로 매핑하기 만하면 \"됩니다. 후자는 한 줄의 인용 부호로 혼합되며 많은 경우 올바르게 구별하기 위해 신중한 검사가 필요합니다.
mtraceur

263

나는 항상 포함 된 각 작은 따옴표를 시퀀스로 바꿉니다 : '\''즉 따옴표 백 슬래시 따옴표는 문자열을 닫고 이스케이프 된 작은 따옴표를 추가하고 문자열을 다시 엽니 다.


필자는 종종 Perl 스크립트에서 "quotify"기능을 사용하여이 기능을 수행합니다. 단계는 다음과 같습니다.

s/'/'\\''/g    # Handle each embedded quote
$_ = qq['$_']; # Surround result with single quotes.

이것은 거의 모든 경우를 처리합니다.

당신이 소개하면 인생은 더 재미있어 eval쉘 스크립트 . 본질적으로 모든 것을 다시 인용해야합니다!

예를 들어, 위의 명령문을 포함하는 quotify라는 Perl 스크립트를 작성하십시오.

#!/usr/bin/perl -pl
s/'/'\\''/g;
$_ = qq['$_'];

그런 다음 이것을 사용하여 올바르게 인용 된 문자열을 생성하십시오.

$ quotify
urxvt -fg '#111111' -bg '#111111'

결과:

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

그런 다음 alias 명령으로 복사 / 붙여 넣을 수 있습니다.

alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

(eval에 명령을 삽입해야하는 경우 quotify를 다시 실행하십시오.

 $ quotify
 alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

결과:

'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''

이것은 평가판에 복사 / 붙여 넣을 수 있습니다.

eval 'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''

1
그러나 이것은 펄이 아닙니다. 그리고 Steve B가 위에서 지적했듯이 "gnu reference manual"을 참조하면 동일한 유형의 따옴표 내에서 bash에서 따옴표를 벗어날 수 없습니다. 실제로 대체 인용 부호로 이스케이프 처리 할 필요가 없습니다. 예를 들어 " '"는 유효한 작은 따옴표 문자열이고 ""'는 이스케이프가없는 유효한 큰 따옴표 문자열입니다.
nicerobot

8
@nicerobot : 나는 다음을 보여주는 예를 추가했습니다 : 1) 동일한 유형의 따옴표 내에서 따옴표를 이스케이프하려고 시도하지 않으며, 2) 또는 다른 따옴표로도 시도하지 않으며, 3) Perl은 유효한 생성 프로세스를 자동화하는 데 사용됩니다 bash 문자열 포함 따옴표
Adrian Pronk

18
첫 번째 단락 자체는 내가 찾던 해답입니다.
Dave Causey 2016 년

9
이 배쉬뿐만 아니라, 입력하는 일입니다 set -x그리고 echo "here's a string"당신은 그 떠들썩한 파티가 실행을 볼 수 있습니다 echo 'here'\''s a string'. ( set +x정상적인 행동을 반환하기 위해)
arekolek

195

Bash 2.04 구문은 $'string'(단지 'string'; 경고 :와 혼동하지 마십시오 $('string')) ANSI C와 같은 이스케이프 시퀀스 를 허용 하고 작은 따옴표 버전으로 확장 할 수있는 또 다른 인용 메커니즘입니다 .

간단한 예 :

  $> echo $'aa\'bb'
  aa'bb

  $> alias myvar=$'aa\'bb'
  $> alias myvar
  alias myvar='aa'\''bb'

귀하의 경우 :

$> alias rxvt=$'urxvt -fg \'#111111\' -bg \'#111111\''
$> alias rxvt
alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

일반적인 이스케이프 시퀀스는 예상대로 작동합니다.

\'     single quote
\"     double quote
\\     backslash
\n     new line
\t     horizontal tab
\r     carriage return

다음은 man bash(버전 4.4)의 관련 문서를 복사하여 붙여 넣은 것입니다 .

$ 'string'형식의 단어는 특별히 취급됩니다. ANSI C 표준에 지정된대로 백 슬래시 이스케이프 문자를 바꾸어 단어를 문자열로 확장합니다. 백 슬래시 이스케이프 시퀀스가있는 경우 다음과 같이 디코딩됩니다.

    \a     alert (bell)
    \b     backspace
    \e
    \E     an escape character
    \f     form feed
    \n     new line
    \r     carriage return
    \t     horizontal tab
    \v     vertical tab
    \\     backslash
    \'     single quote
    \"     double quote
    \?     question mark
    \nnn   the eight-bit character whose value is the octal 
           value nnn (one to three digits)
    \xHH   the eight-bit character whose value is the hexadecimal
           value HH (one or two hex digits)
    \uHHHH the Unicode (ISO/IEC 10646) character whose value is 
           the hexadecimal value HHHH (one to four hex digits)
    \UHHHHHHHH the Unicode (ISO/IEC 10646) character whose value 
               is the hexadecimal value HHHHHHHH (one to eight 
               hex digits)
    \cx    a control-x character

확장 된 결과는 달러 기호가없는 것처럼 작은 따옴표로 묶습니다.


자세한 내용 은 bash-hackers.org 위키에서 따옴표 및 이스케이프 : ANSI C와 유사한 문자열 을 참조하십시오. 또한 "Bash Changes" 파일 ( 여기서는 개요 )에는 $'string'인용 메커니즘 과 관련된 변경 및 버그 수정에 대한 내용이 많이 언급되어 있습니다 .

unix.stackexchange.com에 따르면 일반 문자로 특수 문자를 사용하는 방법은 무엇입니까? bash, zsh, mksh, ksh93 및 FreeBSD 및 busybox sh에서 (일부 변형 사용) 작동해야합니다.


사용할 수 있지만 여기에 작은 따옴표로 묶인 문자열은 실제 작은 따옴표로 묶인 문자열이 아니며,이 문자열의 내용은 쉘에 의해 해석 될 수 있습니다 : echo $'foo\'b!ar'=> !ar': event not found
regilero

2
내 컴퓨터에서 > echo $BASH_VERSION 4.2.47(1)-release > echo $'foo\'b!ar' foo'b!ar
mj41

1
그렇습니다. 그것이 "may"의 이유입니다. Red Hat 6.4, 확실히 오래된 bash 버전이었습니다.
regilero 2016 년

Bash ChangeLog 에는 많은 버그 수정이 포함되어 $'있으므로 가장 쉬운 방법은 이전 시스템에서 직접 시도하는 것입니다.
mj41 2016 년

주의 : tiswww.case.edu/php/chet/bash/CHANGESe. Bash no longer inhibits C-style escape processing ($'...') while performing pattern substitution word expansions. 에서 가져 왔습니다 . 4.3.42에서는 작동하지만 4.3.48에서는 작동하지 않습니다.
stiller_leser

49

나는 그의 블로그 (link pls?)에 항목을 볼 수 없지만 gnu 참조 매뉴얼 에 따르면 :

작은 따옴표 ( '' ')로 문자를 묶으면 따옴표 안에 각 문자의 리터럴 값이 유지됩니다. 백 슬래시가 앞에 있어도 작은 따옴표 사이에는 작은 따옴표가 없을 수 있습니다.

bash는 이해할 수 없습니다.

alias x='y \'z '

그러나 큰 따옴표로 묶으면이 작업을 수행 할 수 있습니다.

alias x="echo \'y "
> x
> 'y


큰 따옴표로 묶은 내용은 평가되므로 liori가 제안한 작은 따옴표 만 큰 따옴표로 묶는 것이 적절한 해결책 인 것 같습니다.
Piotr Dobrogost

3
이것이 실제 질문에 대한 답변입니다. 수락 된 답변이 해결책을 제공 할 수 있지만, 기술적으로 묻지 않은 질문에 답변합니다.
Matthew G

3
매튜, 문제는 작은 따옴표 안에 작은 따옴표를 이스케이프 처리하는 것이 었습니다. 이 답변은 사용자에게 자신의 행동을 바꾸도록 요구하며, 질문 제목에서 알 수 있듯이 큰 따옴표를 사용하는 데 방해가된다면이 답변은 도움이되지 않습니다. 비록 (명확하지만) 매우 유용하지만, 공감할만한 가치가 있지만, 허용 된 답변은 Op가 요구 한 정확한 문제를 해결합니다.
Fernando Cordeiro

큰 따옴표 문자열에서 작은 따옴표를 인용 할 필요가 없습니다.
Matthew D. Scholefield

32

'\''작은 따옴표로 묶은 문자열 내에서 작은 따옴표 를 사용 하는 것이 Bash에서 작동한다는 것을 확인할 수 있으며 스레드의 앞부분에 나오는 "접착제"인수와 같은 방식으로 설명 할 수 있습니다. 따옴표로 묶인 문자열이 있다고 가정 'A '\''B'\'' C'합니다 (여기의 모든 따옴표는 작은 따옴표입니다). 에코로 전달되면 다음을 인쇄합니다 A 'B' C.. 각각 '\''의 첫 번째 따옴표는 현재 작은 따옴표로 묶인 문자열을 닫고, 다음 \'은 작은 따옴표를 이전 문자열에 붙이고 ( \'따옴표로 묶은 문자열을 시작하지 않고 작은 따옴표를 지정하는 방법 임) 마지막 따옴표는 다른 작은 따옴표로 묶인 문자열을 엽니 다.


2
이것은 잘못된 것입니다.이 구문 '\'은 작은 따옴표로 묶인 문자열에 "내부"로 들어 가지 않습니다. 이 문장 'A'\ ''B '\' 'C'에서 당신은 5 \ 이스케이프와 작은 따옴표 문자열을 연결합니다
teknopaul

1
@teknopaul 과제 alias something='A '\''B'\'' C'something단일 문자열이되므로 과제의 오른쪽이 기술적으로 단일 문자열이 아니더라도 그다지 중요하지 않다고 생각합니다.
Teemu Leisti

이것은 귀하의 예제에서 작동하지만 작은 따옴표로 묶은 문자열 안에 작은 따옴표를 삽입하는 방법에 대한 솔루션을 기술적으로 제공 하지는 않습니다 . 이미 설명했지만 그렇습니다 . 즉, 작은 따옴표로 묶은 문자열 안에 작은 따옴표 문자를 삽입하는 솔루션은 그러한 문자열을 자체적으로 만들어 인쇄 할 수 있어야합니다. 그러나이 경우이 솔루션은 작동하지 않습니다. . 설계된대로 BASH는이를 실제로 허용하지 않습니다. 'A ' + ' + 'B' + ' + ' C'STR='\''; echo $STR
krb686

@mikhail_b '\''는 bash 에서 작동합니다. gnu.org/software/bash/manual/bashref.html의 어떤 섹션에서 그러한 동작을 지정 하는지 지적 할 수 있습니까?
Jingguo Yao

20

이스케이프 된 작은 따옴표 문자 (\ ')를 사용하여 연결하거나 작은 따옴표 문자를 큰 따옴표 ( "'")로 묶어 연결하면 두 버전 모두 작동합니다.

질문의 저자는 마지막 탈출 시도 끝에 추가 작은 따옴표 ( ')가 있음을 알지 못했습니다.

alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''
           │         │┊┊|       │┊┊│     │┊┊│       │┊┊│
           └─STRING──┘┊┊└─STRIN─┘┊┊└─STR─┘┊┊└─STRIN─┘┊┊│
                      ┊┊         ┊┊       ┊┊         ┊┊│
                      ┊┊         ┊┊       ┊┊         ┊┊│
                      └┴─────────┴┴───┰───┴┴─────────┴┘│
                          All escaped single quotes    │
                                                       │
                                                       ?

이전의 멋진 ASCII / 유니 코드 아트에서 볼 수 있듯이 마지막 이스케이프 된 작은 따옴표 (\ ') 뒤에 불필요한 작은 따옴표 (')가옵니다. Notepad ++에있는 것과 같은 구문 강조 표시를 사용하면 매우 도움이 될 수 있습니다.

다음과 같은 다른 예에서도 마찬가지입니다.

alias rc='sed '"'"':a;N;$!ba;s/\n/, /g'"'"
alias rc='sed '\'':a;N;$!ba;s/\n/, /g'\'

이 두 개의 아름다운 별칭 인스턴스는 파일을 어떻게 정리할 수 있는지 매우 복잡하고 난독 한 방식으로 표시됩니다. 즉, 줄이 많은 파일에서 이전 줄의 내용 사이에 쉼표와 공백이있는 한 줄만 얻습니다. 이전 주석을 이해하기 위해 다음은 예입니다.

$ cat Little_Commas.TXT
201737194
201802699
201835214

$ rc Little_Commas.TXT
201737194, 201802699, 201835214

3
ASCII 표 삽화를 위해 Upwoted :)
php-dev

16

쉘에서 따옴표를 이스케이프 처리하는 간단한 예 :

$ echo 'abc'\''abc'
abc'abc
$ echo "abc"\""abc"
abc"abc

이미 열린 하나 ( ') 를 마무리하고 이스케이프 된 하나 ( \')를 배치 한 다음 다른 하나를 열면 ( ') 완료됩니다. 이 구문은 모든 명령에 적용됩니다. 첫 번째 답변과 매우 비슷한 접근 방식입니다.


15

글쎄, 때로는 대체 접근법을 고려하는 것이 합리적이기 때문에 인용 문제를 구체적으로 다루고 있지 않습니다.

rxvt() { urxvt -fg "#${1:-000000}" -bg "#${2:-FFFFFF}"; }

그런 다음 다음과 같이 호출 할 수 있습니다.

rxvt 123456 654321

아이디어는 이제 따옴표에 대한 걱정없이 이것을 별칭으로 지정할 수 있다는 것입니다.

alias rxvt='rxvt 123456 654321'

또는 #어떤 이유로 든 모든 통화에 를 포함해야하는 경우 :

rxvt() { urxvt -fg "${1:-#000000}" -bg "${2:-#FFFFFF}"; }

그런 다음 다음과 같이 호출 할 수 있습니다.

rxvt '#123456' '#654321'

물론 별칭은 다음과 같습니다.

alias rxvt="rxvt '#123456' '#654321'"

(죄송합니다. 내가 인용을 해결 한 것 같습니다 :)


1
작은 따옴표 안에 작은 따옴표로 묶은 작은 따옴표 안에 무언가를 넣으려고했습니다. Yikes. "다른 접근 방식을 시도하십시오"라는 답변에 감사드립니다. 그것은 차이를 만들었습니다.
클린턴 블랙 모어

1
5 년 늦었지만 마지막 별칭에 작은 따옴표가 누락되지 않았습니까?
Julien

1
@Julien 나는 문제를 보지 못한다 ;-)
nicerobot

11

작은 따옴표로 묶은 문자열 안에 작은 따옴표를 넣을 수 없으므로 가장 간단하고 읽기 쉬운 옵션은 HEREDOC 문자열을 사용하는 것입니다

command=$(cat <<'COMMAND'
urxvt -fg '#111111' -bg '#111111'
COMMAND
)

alias rxvt=$command

위의 코드에서 HEREDOC는 cat명령으로 전송 되고 그 출력은 명령 대체 표기법을 통해 변수에 할당됩니다.$(..)

HEREDOC 주위에 작은 따옴표를 넣어야합니다. $()


이전에 아래로 스크롤했으면 좋겠습니다.이 접근 방식을 다시 개발하여 게시했습니다. 이것은 다른 모든 탈출 방식보다 훨씬 깨끗하고 읽기 쉽습니다. dashUbuntu 시작 스크립트 및 다른 곳의 기본 쉘 과 같은 일부 비 bash 쉘에서는 작동하지 않습니다 .
Korny

감사합니다! 내가 찾은 것, heredoc을 통해 명령을 정의하고 자동 이스케이프 명령을 ssh에 전달하는 방법. BTW cat << 따옴표없이 명령을 사용하면 명령 내에서 변수를 보간 할 수 있으며이 방법에도 효과적입니다.
Igor Tverdovskiy

10

난 그냥 쉘 코드를 사용 .. 예를 들어, \x27또는 \\x22해당되는 경우. 정말 번거롭지 않습니다.


작동중인 예를 보여 주시겠습니까? 나를 위해 그것은 단지 문자를 인쇄합니다 x27(Centos 6.6에서)
Will Sheppard

6

이 답변의 대부분은 귀하가 요구하는 특정 사례에 부딪칩니다. 이 친구가 일반적인 접근 방식은 내가 임의 필요한 경우 인용을 허용하는 개발에 인용 bash는 명령 ssh를 통해 쉘 확장, 예를 들어, 여러 계층을 통해, su -c, bash -c, 하나 개의 코어 원시 여기 필요가있다 등 네이티브 배쉬에서 :

quote_args() {
    local sq="'"
    local dq='"'
    local space=""
    local arg
    for arg; do
        echo -n "$space'${arg//$sq/$sq$dq$sq$dq$sq}'"
        space=" "
    done
}

이것은 정확히 말한 것을 수행합니다 : 각 인수를 개별적으로 쉘 인용합니다 (물론 bash 확장 후).

$ quote_args foo bar
'foo' 'bar'
$ quote_args arg1 'arg2 arg2a' arg3
'arg1' 'arg2 arg2a' 'arg3'
$ quote_args dq'"'
'dq"'
$ quote_args dq'"' sq"'"
'dq"' 'sq'"'"''
$ quote_args "*"
'*'
$ quote_args /b*
'/bin' '/boot'

하나의 확장 계층에 대해 분명한 작업을 수행합니다.

$ bash -c "$(quote_args echo a'"'b"'"c arg2)"
a"b'c arg2

(큰 따옴표 $(quote_args ...)는 결과를에 대한 단일 인수로 만들기 위해 필요합니다 bash -c.) 더 일반적으로 여러 계층의 확장을 통해 적절하게 인용 할 수 있습니다.

$ bash -c "$(quote_args bash -c "$(quote_args echo a'"'b"'"c arg2)")"
a"b'c arg2

위의 예 :

  1. 쉘은 각 인수를 내부에 quote_args개별적 으로 인용 한 다음 결과를 내부의 큰 따옴표와 함께 단일 인수로 결합합니다.
  2. shell-quotes bash, -c및 1 단계에서 이미 한 번 인용 된 결과를 찾은 다음 결과를 외부 큰 따옴표와 함께 단일 인수로 결합합니다.
  3. 그 혼란을 외부에 대한 논쟁으로 보낸다 bash -c.

그것은 요컨대 아이디어입니다. 이것으로 꽤 복잡한 작업을 수행 할 수 있지만 평가 순서와 인용되는 하위 문자열에 대해주의해야합니다. 예를 들어, 다음은 잘못된 일을합니다 ( "잘못된"에 대한 일부 정의의 경우).

$ (cd /tmp; bash -c "$(quote_args cd /; pwd 1>&2)")
/tmp
$ (cd /tmp; bash -c "$(quote_args cd /; [ -e *sbin ] && echo success 1>&2 || echo failure 1>&2)")
failure

첫번째 예에서, 팽창 배시 즉시 quote_args cd /; pwd 1>&2두 개의 명령어로, quote_args cd /그리고 pwd 1>&2CWD가 여전히 있으므로 /tmp경우에 pwd명령이 실행된다. 두 번째 예는 globbing과 비슷한 문제를 보여줍니다. 실제로 모든 bash 확장에서 동일한 기본 문제가 발생합니다. 여기서 문제는 명령 대체가 함수 호출이 아니라는 것입니다. 문자 그대로 하나의 bash 스크립트를 평가하고 다른 bash 스크립트의 일부로 출력을 사용하는 것입니다.

단순히 쉘 연산자를 이스케이프하려고하면 전달 된 결과 문자열 bash -c이 개별적으로 인용 된 문자열 시퀀스이므로 연산자로 해석되지 않으므로 문자열을 에코하는지 쉽게 알 수 있습니다. bash로 전달되었습니다.

$ (cd /tmp; echo "$(quote_args cd /\; pwd 1\>\&2)")
'cd' '/;' 'pwd' '1>&2'
$ (cd /tmp; echo "$(quote_args cd /\; \[ -e \*sbin \] \&\& echo success 1\>\&2 \|\| echo failure 1\>\&2)")
'cd' '/;' '[' '-e' '*sbin' ']' '&&' 'echo' 'success' '1>&2' '||' 'echo' 'failure' '1>&2'

여기서 문제는 과다 인용하는 것입니다. 필요한 것은 연산자를 엔 클로징에 대한 입력으로 따옴표로 묶지 않는 것 bash -c입니다. 즉, $(quote_args ...)명령 대체 외부에 있어야합니다 .

결과적으로, 가장 일반적인 의미에서 수행해야 할 것은 명령 대체시 별도로 확장되지 않는 명령의 각 단어를 쉘 인용하고 쉘 연산자에 추가 인용을 적용하지 않는 것입니다.

$ (cd /tmp; echo "$(quote_args cd /); $(quote_args pwd) 1>&2")
'cd' '/'; 'pwd' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")
/
$ (cd /tmp; echo "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
'cd' '/'; [ -e *'sbin' ] && 'echo' 'success' 1>&2 || 'echo' 'failure' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
success

이 작업을 마치면 임의의 수준의 평가에 더 인용하기 위해 전체 문자열이 공정합니다.

$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")"
/
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")"
/
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")")"
/
$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")"
success
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *sbin ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")"
success
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")")"
success

기타

이러한 예는 과로 단어처럼 주어진 것처럼 보일 수 있습니다 success, sbin그리고 pwd쉘 인용 할 필요는 없지만, 임의의 입력을 복용하는 스크립트를 작성할 때 키 포인트는 기억하면 당신이 절대적으로 확실하지 않은 모든 인용 할 것입니다 '아무튼를 t는 사용자가 던질 때 당신이 알고하지 않기 때문에, 인용 필요 Robert'; rm -rf /.

표지 아래에서 무슨 일이 일어나고 있는지 더 잘 이해하기 위해 두 가지 작은 도우미 기능으로 놀 수 있습니다.

debug_args() {
    for (( I=1; $I <= $#; I++ )); do
        echo -n "$I:<${!I}> " 1>&2
    done
    echo 1>&2
}

debug_args_and_run() {
    debug_args "$@"
    "$@"
}

명령을 실행하기 전에 각 인수를 명령에 열거합니다.

$ debug_args_and_run echo a'"'b"'"c arg2
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)"
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'bash'"'"' '"'"'-c'"'"' '"'"''"'"'"'"'"'"'"'"'debug_args_and_run'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'echo'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'a"b'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'c'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'arg2'"'"'"'"'"'"'"'"''"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

카일 안녕 귀하의 솔루션은 인수 그룹을 단일 인수로 전달해야 할 때 가지고있는 경우에 훌륭하게 작동했습니다 vagrant ssh -c {single-arg} guest. {single-arg}방랑 게스트 이름으로 뒤에 다음 인수를 필요하기 때문에 요구는 하나의 인수로 취급합니다. 순서는 변경할 수 없습니다. 그러나 나는 안에 명령과 인수를 전달해야했습니다 {single-arg}. 그래서 quote_args()명령과 인수를 인용하고 결과를 큰 따옴표로 묶어 매력처럼 작동했습니다 vagrant ssh -c "'command' 'arg 1 with blanks' 'arg 2'" guest. 감사!!!
Andreas Maier

6

IMHO의 진정한 대답은 작은 따옴표로 묶인 문자열에서 작은 따옴표를 이스케이프 처리 할 수 ​​없다는 것입니다.

그것은 불가능.

우리가 bash를 사용하고 있다고 가정합니다.

bash 매뉴얼에서 ...

Enclosing characters in single quotes preserves the literal value of each
character within the quotes.  A single quote may not occur
between single quotes, even when preceded by a backslash.

다른 문자열 이스케이프 메커니즘 "또는 \ 중 하나를 사용해야합니다.

alias작은 따옴표를 사용한다는 요구에는 마법이 없습니다 .

다음은 모두 bash에서 작동합니다.

alias rxvt="urxvt -fg '#111111' -bg '#111111'"
alias rxvt=urxvt\ -fg\ \'#111111\'\ -bg\ \'#111111\'

후자는 \를 사용하여 공백 문자를 이스케이프합니다.

작은 따옴표가 필요한 # 111111에 대한 마법은 없습니다.

다음 옵션은 rxvt 별명이 예상대로 작동한다는 점에서 다른 두 옵션과 동일한 결과를 얻습니다.

alias rxvt='urxvt -fg "#111111" -bg "#111111"'
alias rxvt="urxvt -fg \"#111111\" -bg \"#111111\""

번거로운 #을 직접 벗어날 수도 있습니다.

alias rxvt="urxvt -fg \#111111 -bg \#111111"

"정답은 작은 따옴표로 묶은 문자열에서 작은 따옴표를 이스케이프 할 수 없다는 것입니다." 그것은 기술적으로 사실입니다. 그러나 작은 따옴표로 시작하고 작은 따옴표로 끝나고 중간에 작은 따옴표 만 포함하는 솔루션을 가질 수 있습니다. stackoverflow.com/a/49063038
wisbucky

탈출로가 아니라 연결로만 가능합니다.
teknopaul

4

주어진 예에서 외부 이스케이프 메커니즘으로 작은 따옴표 대신 큰 따옴표를 사용했습니다.

alias rxvt="urxvt -fg '#111111' -bg '#111111'"

이 접근법은 고정 문자열을 명령에 전달하려는 많은 경우에 적합합니다. 쉘이 큰 따옴표로 묶인 문자열을 어떻게 해석하는지 확인하십시오. echo 하고 필요한 경우 백 슬래시로 문자를 이스케이프 처리하십시오.

이 예에서는 큰 따옴표만으로도 문자열을 보호 할 수 있습니다.

$ echo "urxvt -fg '#111111' -bg '#111111'"
urxvt -fg '#111111' -bg '#111111'

4

분명히 큰 따옴표로 묶는 것이 더 쉬울 것입니다.하지만 그 도전은 어디에 있습니까? 작은 따옴표 만 사용하는 대답은 다음과 같습니다. alias증거 대신 인쇄하는 것이 더 쉽지만 대신 변수를 사용하고 있지만를 사용하는 것과 같습니다 alias.

$ rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'
$ echo $rxvt
urxvt -fg '#111111' -bg '#111111'

설명

핵심은 작은 따옴표를 닫고 원하는 횟수만큼 다시 열 수 있다는 것입니다. 예를 foo='a''b'들어와 같습니다 foo='ab'. 따라서 작은 따옴표를 닫고 리터럴 작은 따옴표를 던져 \'다음 작은 따옴표를 다시 열 수 있습니다.

고장 다이어그램

이 다이어그램은 괄호를 사용하여 작은 따옴표가 열리고 닫히는 위치를 보여줍니다. 따옴표는 괄호처럼 "중첩"되지 않습니다. 색상 강조 표시에주의를 기울여 올바르게 적용 할 수도 있습니다. 인용 된 문자열은 적갈색 인 반면, 따옴표 \'는 검은 색입니다.

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'    # original
[^^^^^^^^^^] ^[^^^^^^^] ^[^^^^^] ^[^^^^^^^] ^    # show open/close quotes
 urxvt -fg   ' #111111  '  -bg   ' #111111  '    # literal characters remaining

(이것은 본질적으로 Adrian의 답변과 동일하지만 이것이 더 잘 설명되어 있다고 생각합니다. 그의 답변에는 끝에 2 개의 불필요한 작은 따옴표가 있습니다.)


'\''방법 을 사용하기 위해 +1 나는 '"'"'종종 사람들이 읽기 어려운 방법 보다 권장합니다 .
mtraceur

3

다음은 위에서 언급 한 하나의 정답에 대한 설명입니다.

때로는 ssh를 통해 rsync를 사용하여 다운로드 할 것이고 'TWICE에'가있는 파일 이름을 이스케이프해야합니다! (OMG!) bash는 한 번, ssh는 한 번입니다. 인용 구분 기호를 대체하는 동일한 원칙이 여기에 있습니다.

예를 들어 Louis Theroux의 LA Stories ...를 얻고 싶다고 가정 해 봅시다.

  1. 먼저 Louis Theroux를 bash의 작은 따옴표와 ssh의 큰 따옴표로 묶습니다 : ' "Louis Theroux"'
  2. 그런 다음 작은 따옴표를 사용하여 큰 따옴표 ' "'를 이스케이프 처리하십시오.
  3. 아포스트로피 " '"를 피하기 위해 큰 따옴표 사용
  4. 그런 다음 작은 따옴표를 사용하여 큰 따옴표 ' "'를 이스케이프 처리하여 # 2를 반복하십시오.
  5. 그런 다음 bash에 대해서는 LA Stories를 작은 따옴표로 묶고 ssh에 대해서는 큰 따옴표로 묶으십시오 : ' "LA Stories"'

보라! 당신은 이것으로 마무리합니다 :

rsync -ave ssh '"Louis Theroux"''"'"'"'"''"s LA Stories"'

그것은 하나의 작은 일에 대한 끔찍한 많은 일입니다.


3
shell_escape () {
    printf '%s' "'${1//\'/\'\\\'\'}'"
}

구현 설명 :

  • 작은 따옴표를 감싸서 쉽게 출력하고 ${...}구문을 사용할 수 있도록 큰 따옴표

  • bash의 검색 및 바꾸기는 다음과 같습니다. ${varname//search/replacement}

  • 우리는 교체하고 '함께'\''

  • '\''단일을 다음 '과 같이 인코딩합니다 .

    1. ' 작은 따옴표를 종료

    2. \'인코딩합니다 '(따옴표 안에 있지 않기 때문에 백 슬래시가 필요합니다)

    3. ' 작은 따옴표를 다시 시작합니다

    4. bash는 공백없이 자동으로 문자열을 연결합니다.

  • 거기의 \모든 전에 \그리고 '그 때문에의 탈출 규칙입니다 ${...//.../...}.

string="That's "'#@$*&^`(@#'
echo "original: $string"
echo "encoded:  $(shell_escape "$string")"
echo "expanded: $(bash -c "echo $(shell_escape "$string")")"

PS 큰 따옴표로 묶인 문자열보다 훨씬 단순하기 때문에 항상 작은 따옴표로 묶은 문자열로 인코딩하십시오.


2

너무 많은 중첩 인용 계층 문제를 해결하는 또 다른 방법 :

너무 작은 공간에 너무 많이 넣으려고하므로 bash 함수를 사용하십시오.

문제는 중첩 수준이 너무 높으며 기본 별칭 기술이 수용하기에 충분히 강력하지 않다는 것입니다. 다음과 같이 bash 함수를 사용하여 작은 따옴표와 따옴표를 다시 전달하고 매개 변수로 전달 된 값을 모두 정상적으로 처리합니다.

lets_do_some_stuff() {
    tmp=$1                       #keep a passed in parameter.
    run_your_program $@          #use all your passed parameters.
    echo -e '\n-------------'    #use your single quotes.
    echo `date`                  #use your back ticks.
    echo -e "\n-------------"    #use your double quotes.
}
alias foobarbaz=lets_do_some_stuff

그런 다음 $ 1 및 $ 2 변수와 작은 따옴표 및 백 틱을 사용하여 앨리어스 함수가 ​​무결성을 손상시키지 않아도 걱정할 수 있습니다.

이 프로그램은 다음을 인쇄합니다.

el@defiant ~/code $ foobarbaz alien Dyson ring detected @grid 10385
alien Dyson ring detected @grid 10385
-------------
Mon Oct 26 20:30:14 EDT 2015
-------------

2

GNU Parallel이 설치되어 있다면 내부 인용을 사용할 수 있습니다 :

$ parallel --shellquote
L's 12" record
<Ctrl-D>
'L'"'"'s 12" record'
$ echo 'L'"'"'s 12" record'
L's 12" record

20190222 버전부터 --shellquote여러 번 할 수도 있습니다 .

$ parallel --shellquote --shellquote --shellquote
L's 12" record
<Ctrl-D>
'"'"'"'"'"'"'L'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'s 12" record'"'"'"'"'"'"'
$ eval eval echo '"'"'"'"'"'"'L'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'s 12" record'"'"'"'"'"'"'
L's 12" record

지원되는 모든 쉘에서 문자열을 인용합니다 (뿐만 아니라 bash).


1

이 기능 :

quote () 
{ 
    local quoted=${1//\'/\'\\\'\'};
    printf "'%s'" "$quoted"
}

'내부 인용을 허용합니다 '. 이것으로 사용하십시오 :

$ quote "urxvt -fg '#111111' -bg '#111111'"
'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

작은 따옴표와 큰 따옴표가 섞인 것처럼 따옴표로 묶는 행이 더 복잡해지면 변수 안에서 문자열을 따옴표로 묶는 것이 매우 까다로울 수 있습니다. 이러한 경우가 나타나면 스크립트 안에 인용해야 할 정확한 줄을 작성하십시오 (이와 유사).

#!/bin/bash

quote ()
{
    local quoted=${1//\'/\'\\\'\'};
    printf "'%s'" "$quoted"
}

while read line; do
    quote "$line"
done <<-\_lines_to_quote_
urxvt -fg '#111111' -bg '#111111'
Louis Theroux's LA Stories
'single quote phrase' "double quote phrase"
_lines_to_quote_

출력합니다 :

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
'Louis Theroux'\''s LA Stories'
''\''single quote phrase'\'' "double quote phrase"'

작은 따옴표 안에 올바르게 인용 된 모든 문자열


1

Python 2 또는 Python 3에서 셸 문자열을 생성하는 경우 다음이 인수를 인용하는 데 도움이 될 수 있습니다.

#!/usr/bin/env python

from __future__ import print_function

try:  # py3
    from shlex import quote as shlex_quote
except ImportError:  # py2
    from pipes import quote as shlex_quote

s = """foo ain't "bad" so there!"""

print(s)
print(" ".join([shlex_quote(t) for t in s.split()]))

출력됩니다 :

foo ain't "bad" so there!
foo 'ain'"'"'t' '"bad"' so 'there!'

1

내 두 센트는 다음과 같습니다. sh단지 bash특정 하지 않고 이식 가능하게 하려는 경우 (외부 프로그램을 시작하기 때문에 솔루션이 너무 효율적이지 않습니다- sed) :

  • 이것을 당신의 어딘가에 quote.sh(또는 그냥 quote) 넣으 십시오 PATH:
# 이것은 표준 입력 (stdin)에서 작동합니다
인용문() {
  echo -n " ​​'";
  sed 's / \ ([' " '"'] [ ' "'" '] * \) /' " '"' "\ 1" ' "'" '/ g';
  echo -n " ​​'"
}

"$ 1"사례
 -) 인용문 ;;
 *) echo "사용법 : cat ... | quote-# Bourne 쉘의 작은 따옴표 입력"2> & 1 ;;
esac

예를 들면 :

$ echo -n "Geday, 메이트!" | ./quote.sh-
'G' " '"'일, 친구! '

그리고 물론 그것은 다시 변환됩니다.

$ echo 'G' " '"'day, 친구! '
G'day, 친구!

설명 : 기본적으로 우리는 따옴표 입력을 묶으해야 '하고이 마이크로 몬스터 내의 모든 따옴표를 대체 : '"'"'(페어링로 개방 견적을 끝 '의 큰 따옴표로 포장하여 작은 따옴표를 발견 탈출 - "'", 및 그리고 마침내 새로운 개방 따옴표 발행 ', 또는 의사 표기법을 : ' + "'" + ' == '"'"')

이를 수행하는 한 가지 표준 방법 sed은 다음 대체 명령과 함께 사용하는 것입니다 .

s/\(['][']*\)/'"\1"'/g 

그러나 하나의 작은 문제는 쉘에서 그것을 사용하려면 sed 표현식 자체 에서이 작은 따옴표 문자를 모두 이스케이프해야한다는 것입니다.

sed 's/\(['"'"']['"'"']*\)/'"'"'"\1"'"'"'/g' 

(이 결과를 작성하는 좋은 방법은 s/\(['][']*\)/'"\1"'/gKyle Rose 또는 George V. Reilly의 스크립트에 원래 표현 을 제공하는 것입니다).

마지막으로, stdin명령 줄 인수를 통해 전달하는 것은 이미 너무 많은 문제가 될 수 있기 때문에 입력이 올 것으로 예상하는 것이 합리적 입니다.

(아, 누군가가 스크립트를 실행했을 때 스크립트가 ./quote.sh --help무엇 을 하는지 궁금해 할 때 스크립트가 멈추지 않도록 작은 도움말 메시지를 추가하고 싶을 수도 있습니다 .


0

다른 해결책이 있습니다. 이 함수는 위의 투표 응답이 설명하는 것처럼 단일 인수를 사용하고 작은 따옴표 문자를 사용하여 적절하게 인용합니다.

single_quote() {
  local quoted="'"
  local i=0
  while [ $i -lt ${#1} ]; do
    local ch="${1:i:1}"
    if [[ "$ch" != "'" ]]; then
      quoted="$quoted$ch"
    else
      local single_quotes="'"
      local j=1
      while [ $j -lt ${#1} ] && [[ "${1:i+j:1}" == "'" ]]; do
        single_quotes="$single_quotes'"
        ((j++))
      done
      quoted="$quoted'\"$single_quotes\"'"
      ((i+=j-1))
    fi
    ((i++))
  done
  echo "$quoted'"
}

따라서 다음과 같이 사용할 수 있습니다.

single_quote "1 2 '3'"
'1 2 '"'"'3'"'"''

x="this text is quoted: 'hello'"
eval "echo $(single_quote "$x")"
this text is quoted: 'hello'
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.