Bash를 사용할 때 어떤 문자를 이스케이프해야합니까?


206

Bash에서 이스케이프해야하는 포괄적 인 문자 목록이 있습니까? 그냥 확인할 수 있습니까 sed?

특히 %탈출 해야하는지 여부를 확인하고있었습니다 . 나는 시도했다

echo "h%h" | sed 's/%/i/g'

탈출하지 않고 잘 작동했습니다 %. %탈출 할 필요가 없다는 의미 입니까? 이것이 필요성을 점검하는 좋은 방법 이었습니까?

그리고 더 일반적인 : 그들은 탈출하기 위해 동일한 문자입니다 shellbash?


4
일반적으로 관심이 있다면 잘못하고있는 것입니다. 데이터 처리에는 코드에 사용되는 구문 분석 및 평가 프로세스를 통해 데이터를 실행하여 이스케이프 처리를 수행해서는 안됩니다. 이것은 SQL에 대한 모범 사례와 매우 유사합니다. 올바른 것은 바인딩 변수를 사용하고 잘못된 것은 문자열 대체를 통해 주입 된 데이터를 "위생 화"하는 것입니다.
Charles Duffy


8
@CharlesDuffy 그래, 때로는 준비된 문장 엔진이 백엔드에서하고있는 일은 그냥 탈출하는 것입니다. SO가 브라우저에 주석을 표시하기 전에 사용자가 제출 한 주석을 이스케이프하므로 "잘못된 작업"입니까? 아니요. XSS를 막고 있습니다. 전혀 신경 쓰지 않는 것은 잘못하고 있습니다.
Parthian Shot

@ParthianShot, 준비된 명령문 엔진이 코드에서 데이터를 완전히 대역 외로 유지하지 않는 경우 해당 코드를 작성한 사람을 쏴야합니다. 예, MySQL의 유선 프로토콜이 그런 식으로 구현 된 것을 알고 있습니다. 내 진술서
찰스 더피

@CharlesDuffy 그리고 내 요점-때로는 당신의 선택은 순전히 귀를 기울일 도구 체인을 사용하여 안전하게 작동하거나 시간과 노력을 8 배 가라 앉혀서 여전히 똑바로서는 것입니다.
Parthian Shot

답변:


282

쉽고 안전한 두 가지 규칙이 sh있습니다 bash.

1. 전체 문자열을 작은 따옴표로 묶습니다.

작은 따옴표 자체를 제외한 모든 문자에 적용됩니다. 작은 따옴표를 이스케이프하려면 따옴표를 닫고 작은 따옴표를 삽입 한 후 따옴표를 다시여십시오.

'I'\''m a s@fe $tring which ends in newline
'

sed 명령 : sed -e "s/'/'\\\\''/g; 1s/^/'/; \$s/\$/'/"

2. 백 슬래시로 모든 문자를 이스케이프

이것은 개행을 제외한 모든 문자에 적용됩니다. 줄 바꿈 문자의 경우 작은 따옴표 나 큰 따옴표를 사용하십시오. 빈 문자열은 여전히 ​​처리해야합니다.""

\I\'\m\ \a\ \s\@\f\e\ \$\t\r\i\n\g\ \w\h\i\c\h\ \e\n\d\s\ \i\n\ \n\e\w\l\i\n\e"
"

sed 명령 : sed -e 's/./\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'.

2b. 더 읽기 쉬운 버전 2

와 같은 쉽고 안전한 문자 집합 [a-zA-Z0-9,._+:@%/-]이 있습니다.

I\'m\ a\ s@fe\ \$tring\ which\ ends\ in\ newline"
"

sed 명령 : LC_ALL=C sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'.


sed 프로그램에서는 마지막 입력 행이 줄 바꿈 바이트로 끝나는 지 여부를 알 수 없습니다 (빈 경우 제외). 그래서 위의 sed 명령이 그렇지 않다고 가정합니다. 인용 된 줄 바꿈을 수동으로 추가 할 수 있습니다.

쉘 변수는 POSIX 의미의 텍스트에 대해서만 정의됩니다. 이진 데이터 처리가 정의되지 않았습니다. 중요한 구현의 경우 바이너리는 NUL 바이트를 제외하고 작동합니다 (변수는 C 문자열로 구현되고 C 문자열, 즉 프로그램 인수로 사용되기 때문). latin1과 같은 "이진"로캘로 전환해야합니다 .


(의 POSIX 사양을 읽으면 규칙을 쉽게 확인할 수 있습니다 sh. bash의 경우 @AustinPhillips에 의해 링크 된 참조 매뉴얼을 확인하십시오)


참고 : # 1의 좋은 변형은 여기에서 볼 수 있습니다 : github.com/scop/bash-completion/blob/… . 실행할 필요는 없지만 필요 sed합니다 bash.
jwd

4
OSX에서 얻는 sed의 풍미가 sed 명령을 제대로 실행하지 않는 것처럼 보입니다. 그들은 리눅스에서 잘 작동합니다!
dalelane

@dalelane : 여기서 테스트 할 수 없습니다. 두 가지 버전 모두에서 작동하는 버전을 편집하십시오.
Jo So

문자열이 '-'(빼기)로 시작해야합니까, 아니면 파일 이름에만 적용됩니까? 후자의 경우 앞에 './'가 필요합니다.
slashmais

무슨 말인지 잘 모르겠습니다. 이러한 sed 명령을 사용하면 입력 문자열이 stdin에서 가져옵니다.
Jo So

59

쉘 입력으로 재사용 할 수있는 형식

특별 printf 형식 지시자 ( %q요청 이런 종류의 내장은) :

printf [-v var] 형식 [인수]

 %q     causes printf to output the corresponding argument
        in a format that can be reused as shell input.

일부 샘플 :

read foo
Hello world
printf "%q\n" "$foo"
Hello\ world

printf "%q\n" $'Hello world!\n'
$'Hello world!\n'

변수를 통해서도 사용할 수 있습니다 :

printf -v var "%q" "$foo
"
echo "$var"
$'Hello world\n'

모든 (128) ASCII 바이트로 빠른 검사 :

128에서 255까지의 모든 바이트를 이스케이프해야합니다.

for i in {0..127} ;do
    printf -v var \\%o $i
    printf -v var $var
    printf -v res "%q" "$var"
    esc=E
    [ "$var" = "$res" ] && esc=-
    printf "%02X %s %-7s\n" $i $esc "$res"
done |
    column

이것은 다음과 같이 렌더링해야합니다.

00 E ''         1A E $'\032'    34 - 4          4E - N          68 - h      
01 E $'\001'    1B E $'\E'      35 - 5          4F - O          69 - i      
02 E $'\002'    1C E $'\034'    36 - 6          50 - P          6A - j      
03 E $'\003'    1D E $'\035'    37 - 7          51 - Q          6B - k      
04 E $'\004'    1E E $'\036'    38 - 8          52 - R          6C - l      
05 E $'\005'    1F E $'\037'    39 - 9          53 - S          6D - m      
06 E $'\006'    20 E \          3A - :          54 - T          6E - n      
07 E $'\a'      21 E \!         3B E \;         55 - U          6F - o      
08 E $'\b'      22 E \"         3C E \<         56 - V          70 - p      
09 E $'\t'      23 E \#         3D - =          57 - W          71 - q      
0A E $'\n'      24 E \$         3E E \>         58 - X          72 - r      
0B E $'\v'      25 - %          3F E \?         59 - Y          73 - s      
0C E $'\f'      26 E \&         40 - @          5A - Z          74 - t      
0D E $'\r'      27 E \'         41 - A          5B E \[         75 - u      
0E E $'\016'    28 E \(         42 - B          5C E \\         76 - v      
0F E $'\017'    29 E \)         43 - C          5D E \]         77 - w      
10 E $'\020'    2A E \*         44 - D          5E E \^         78 - x      
11 E $'\021'    2B - +          45 - E          5F - _          79 - y      
12 E $'\022'    2C E \,         46 - F          60 E \`         7A - z      
13 E $'\023'    2D - -          47 - G          61 - a          7B E \{     
14 E $'\024'    2E - .          48 - H          62 - b          7C E \|     
15 E $'\025'    2F - /          49 - I          63 - c          7D E \}     
16 E $'\026'    30 - 0          4A - J          64 - d          7E E \~     
17 E $'\027'    31 - 1          4B - K          65 - e          7F E $'\177'
18 E $'\030'    32 - 2          4C - L          66 - f      
19 E $'\031'    33 - 3          4D - M          67 - g      

첫 번째 필드가 바이트의 16 진수 값인 E경우 두 번째는 문자를 이스케이프해야하는 경우 포함 하고 세 번째 필드는 이스케이프 된 문자 표시를 표시합니다.

,?

당신은하지 않는 일부 문자를 볼 수 항상 같은 이스케이프 할 필요를 ,, }하고 {.

그래서하지 항상 하지만 언젠가 :

echo test 1, 2, 3 and 4,5.
test 1, 2, 3 and 4,5.

또는

echo test { 1, 2, 3 }
test { 1, 2, 3 }

그러나주의 :

echo test{1,2,3}
test1 test2 test3

echo test\ {1,2,3}
test 1 test 2 test 3

echo test\ {\ 1,\ 2,\ 3\ }
test  1 test  2 test  3

echo test\ {\ 1\,\ 2,\ 3\ }
test  1, 2 test  3 

이 pritnf를 통해 bash는 / 쉬를 호출, 문자열이 처음 쉘이 bash는 / 쉬 위해 탈출해야 문제가
ThorSummoner

1
@ThorSummoner, 문자열을 다른 언어의 쉘에 리터럴 인수로 전달하는 경우가 아닙니다 (아마 인용 방법을 이미 알고있을 것입니다). 파이썬에서 : subprocess.Popen(['bash', '-c', 'printf "%q\0" "$@"', '_', arbitrary_string], stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate()적절하게 쉘 인용 버전을 제공 arbitrary_string합니다.
찰스 더피

1
FYI bash 's %q오랫동안 깨졌습니다-내 마음이 잘 봉사하면 2013 년 ~ 10 년 동안 부서진 후에 오류가 수정되었지만 여전히 깨질 수 있습니다. 따라서 그것에 의존하지 마십시오.
Jo So

@CharlesDuffy 물론, 파이썬 땅에 들어가면 shlex.quote()(> = 3.3, pipes.quote()-문서화되지 않은-구 버전의 경우) 작업을 수행하고 대부분의 문자열을보다 사람이 읽을 수있는 버전 (필요에 따라 따옴표와 이스케이프 추가)을 생성합니다. 쉘을 생성하지 않아도됩니다.
토마스 펄

1
에 대한 특별한 메모를 추가해 주셔서 감사합니다 ,. 나는이 내장 된 것을 배쉬 배우고 놀랐습니다 printf -- %q ','제공 \,하지만, /usr/bin/printf -- %q ','제공 ,(유엔 - escapted). 다른 문자에 동일 : {, |, }, ~.
kevinarpe

34

bash 에서 다른 사람이 RTFM을 갖지 못하게하려면 :

큰 따옴표 문자를 묶는 것은 제외하고, 따옴표 내의 모든 문자의 리터럴 값을 보존 $, `, \,와, 역사 확장을 사용하는 경우, !.

... 따라서 그것들을 피하고 (물론 인용문 자체도) 괜찮을 것입니다.

좀 더 보수적 인 '의심 할 때 이스케이프'접근 방식을 사용하는 경우 식별자 문자 (예 : ASCII 문자, 숫자 또는 '_')를 이스케이프 처리하지 않고 특별한 의미의 문자를 가져 오는 것을 피할 수 있습니다. 이것들은 (즉, 이상한 POSIX-ish 쉘에서) 특별한 의미를 가지지 않으므로 탈출해야 할 가능성은 거의 없습니다.



이것은 짧고 달콤하며 대부분 정답 (+1)이지만 작은 따옴표를 사용하는 것이 더 좋습니다. 긴 답변을 참조하십시오.
Jo So

26

print '%q' 기술을 사용하여 루프를 실행하여 어떤 문자가 특별한 지 알아낼 수 있습니다.

#!/bin/bash
special=$'`!@#$%^&*()-_+={}|[]\\;\':",.<>?/ '
for ((i=0; i < ${#special}; i++)); do
    char="${special:i:1}"
    printf -v q_char '%q' "$char"
    if [[ "$char" != "$q_char" ]]; then
        printf 'Yes - character %s needs to be escaped\n' "$char"
    else
        printf 'No - character %s does not need to be escaped\n' "$char"
    fi
done | sort

이 출력을 제공합니다.

No, character % does not need to be escaped
No, character + does not need to be escaped
No, character - does not need to be escaped
No, character . does not need to be escaped
No, character / does not need to be escaped
No, character : does not need to be escaped
No, character = does not need to be escaped
No, character @ does not need to be escaped
No, character _ does not need to be escaped
Yes, character   needs to be escaped
Yes, character ! needs to be escaped
Yes, character " needs to be escaped
Yes, character # needs to be escaped
Yes, character $ needs to be escaped
Yes, character & needs to be escaped
Yes, character ' needs to be escaped
Yes, character ( needs to be escaped
Yes, character ) needs to be escaped
Yes, character * needs to be escaped
Yes, character , needs to be escaped
Yes, character ; needs to be escaped
Yes, character < needs to be escaped
Yes, character > needs to be escaped
Yes, character ? needs to be escaped
Yes, character [ needs to be escaped
Yes, character \ needs to be escaped
Yes, character ] needs to be escaped
Yes, character ^ needs to be escaped
Yes, character ` needs to be escaped
Yes, character { needs to be escaped
Yes, character | needs to be escaped
Yes, character } needs to be escaped

,조금 의심스러운 것처럼 보이는 결과도 있습니다. @CharlesDuffy의 입력을 얻는 것이 흥미로울 것입니다.


2
내 답변 의 마지막 단락에서 ,조금 의심스러운 답변을 읽을 수 있습니다.
F. Hauri

2
명심 %q당신이 문자를 사용하기 위해 기획 곳 쉘 내에서이 가능한 쉘 컨텍스트에서 특별한 의미를 가질 수있는 모든 문자를 이스케이프 있도록, 알 수 없습니다. ,그녀는 쉘에 자체는 특별한 의미가 없습니다 만 F.Hauri 그의 대답에 지적 @, 그것은 내에서 특별한 의미를 가지고있다 {...}중괄호 확장 : gnu.org/savannah-checkouts/gnu/bash/manual/... 이처럼! 일반적으로가 아니라 특정 상황에서만 확장이 필요합니다. 제대로 echo Hello World!작동하지만 echo test!test실패합니다.
Mecki

18

이스케이프가 필요한 문자는 Bourne 또는 POSIX 셸에서 Bash와 다릅니다. 일반적으로 (매우) Bash는 해당 쉘의 상위 세트이므로 shellBash에서 탈출해야합니다.

좋은 규칙은 "의심 스럽다면 탈출"입니다. 그러나 일부 문자를 이스케이프하면와 같은 특별한 의미가 \n있습니다. 이들은 man bash아래 페이지에 Quoting나와 있습니다.echo 있습니다.

그 외에는 영숫자가 아닌 문자를 피하십시오. 더 안전합니다. 나는 단 하나의 결정적인 목록을 모른다.

매뉴얼 페이지는 한 곳에는 없지만 어딘가에 나열되어 있습니다. 언어를 배우십시오, 그것이 확실한 방법입니다.

나를 사로 잡은 것은이다 !. 이것은 Bash (및 csh)의 특수 문자 (역사 확장)이지만 Korn 쉘에는 없습니다. 심지어 echo "Hello world!"문제가 있습니다. 평소와 같이 작은 따옴표를 사용하면 특별한 의미가 제거됩니다.


1
나는 좋은 일반 규칙이 "의심 스럽다면 그것을 피하라"는 조언을 특히 좋아한다 . 점검 sed이 탈출 해야하는지 알기에 충분한 지 여전히 의심의 여지 가 있습니다. 답변 주셔서 감사합니다!
fedorqui 'SO 중지 피해'

2
@ fedorqui : 확인 sed이 필요하지 않습니다, 당신은 거의 아무것도 확인할 수 있습니다. sed문제가 아닙니다 bash. 작은 따옴표 안에는 특수 문자가 없습니다 (작은 따옴표 제외), 이스케이프 문자도 이스케이프 할 수 없습니다. sedRE 메타 문자는 안전을 위해 쉘 메타 문자와 너무 많은 중복을 가지고 있기 때문에 명령은 일반적으로 작은 따옴표 안에 있어야한다. 쉘 변수를 임베드 할 때는 예외로주의해야합니다.
cdarke

5
로 확인하십시오 echo. 넣은 것을 꺼내면 도망 갈 필요가 없습니다. :)
Mark Reed

6

bash 문자열에 대해 이야기하고 있다고 가정합니다. 이스케이프에 대한 요구 사항이 다른 여러 유형의 문자열이 있습니다. 예. 작은 따옴표 문자열은 큰 따옴표 문자열과 다릅니다.

가장 좋은 참조는 인용입니다 bash 매뉴얼 섹션입니다.

이스케이프해야하는 문자를 설명합니다. 히스토리 확장과 같이 사용 가능한 옵션에 따라 일부 문자를 이스케이프해야 할 수도 있습니다.


3
따라서 탈출이 쉬운 해결책이없는 정글 이라는 것을 확인하고 각 사례를 확인해야합니다. 감사!
fedorqui 'SO 중지 피해'

@fedorqui 다른 언어와 마찬가지로 준수해야 할 규칙이 있습니다. bash 문자열 이스케이프의 경우 매뉴얼에 설명 된대로 규칙 세트가 매우 작습니다. 이스케이프 할 필요가 없으므로 사용하기 가장 쉬운 문자열은 작은 따옴표입니다. 그러나 작은 따옴표로 묶은 문자열에 작은 따옴표를 포함시킬 방법이 없습니다.
Austin Phillips

@fedorqui. 정글 이 아닙니다 . 탈출은 꽤 가능합니다. 내 새 게시물을 참조하십시오.
Jo So

@fedorqui 작은 따옴표로 묶은 문자열 안에 작은 따옴표를 사용할 수는 없지만 'text' " '"'more text '
CR

4

자동 완성을 사용할 때 bash가 자동으로 일부 문자를 이스케이프하는 것으로 나타났습니다.

예를 들어,라는 디렉토리가 있으면 dir:Abash는 다음과 같이 자동 완성됩니다.dir\:A

이를 사용하여 ASCII 테이블의 문자를 사용하여 몇 가지 실험을 실행하고 다음 목록을 도출했습니다.

자동 완성시 bash가 이스케이프되는 문자 : (공백 포함)

 !"$&'()*,:;<=>?@[\]^`{|}

bash가 이스케이프하지 않는 문자 :

#%+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~

( /디렉토리 이름에 사용할 수 없으므로 제외 했습니다)


2
당신이 정말로 포괄적 인 목록을 원한다면, 어떤 문자 printf %q가 인수로 전달되는지, 이상적으로는 전체 문자 세트를 거치는 경우 수정하지 않는 것이 좋습니다 .
Charles Duffy

아포스트로피 문자열이 있어도 특수 문자를 생성하기 위해 문자와 숫자를 이스케이프 처리 할 수 ​​있습니다. 예를 들어, tr '\ n' '\ t'는 개행 문자를 탭 문자로 변환합니다.
Dick Guertin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.