os.system () 호출을 이스케이프하는 방법은 무엇입니까?


124

os.system ()을 사용할 때 종종 명령에 매개 변수로 전달 된 파일 이름 및 기타 인수를 이스케이프해야합니다. 어떻게 할 수 있습니까? 여러 운영 체제 / 쉘에서 작동하지만 특히 bash에서는 작동하는 것이 좋습니다.

저는 현재 다음을 수행하고 있지만이를위한 라이브러리 함수 또는 적어도 더 우아하고 / 튼튼하고 / 효율적인 옵션이 있어야합니다.

def sh_escape(s):
   return s.replace("(","\\(").replace(")","\\)").replace(" ","\\ ")

os.system("cat %s | grep something | sort > %s" 
          % (sh_escape(in_filename), 
             sh_escape(out_filename)))

편집 : 따옴표 사용에 대한 간단한 대답을 받아 들였습니다. 왜 그렇게 생각하지 않았는지 모르겠습니다. 나는 Windows에서 왔기 때문에 '와 "가 약간 다르게 작동하는 것 같습니다.

보안과 관련하여 우려 사항을 이해하지만이 경우 os.system ()이 제공하는 빠르고 쉬운 솔루션에 관심이 있으며 문자열의 소스는 사용자가 생성하지 않았거나 최소한 사용자가 입력 한 것입니다. 신뢰할 수있는 사용자 (나).


1
보안 문제에주의하십시오! 예를 들어 out_filename이 foo.txt 인 경우; rm -rf / 악의적 인 사용자가 쉘에서 직접 해석하는 명령을 더 추가 할 수 있습니다.
스티브 Gury

6
이것은 또한 하위 프로세스가 옵션이 아닌 상황에서 os.system 없이도 유용합니다. 예 : 쉘 스크립트 생성.

이상적인 sh_escape함수는 ;및 공백을 이스케이프 하고 .NET Framework와 같은 파일을 생성하여 보안 문제를 제거하는 것 foo.txt\;\ rm\ -rf\ /입니다.

거의 모든 경우에 os.system이 아닌 하위 프로세스를 사용해야합니다. os.system을 호출하는 것은 주입 공격을 요청하는 것입니다.
allyourcode

답변:


85

이것이 내가 사용하는 것입니다.

def shellquote(s):
    return "'" + s.replace("'", "'\\''") + "'"

쉘은 항상 따옴표로 묶인 파일 이름을 받아들이고 문제의 프로그램에 전달하기 전에 주변 따옴표를 제거합니다. 특히 이것은 공백이나 기타 불쾌한 쉘 메타 문자를 포함하는 파일 이름의 문제를 방지합니다.

업데이트 : Python 3.3 이상을 사용하는 경우 직접 롤링하는 대신 shlex.quote 를 사용 하십시오.


7
@pixelbeat : 이것이 바로 그가 작은 따옴표를 닫고 이스케이프 된 리터럴 작은 따옴표를 추가 한 다음 작은 따옴표를 다시 여는 이유입니다.
lhunath

4
이것이 shellquote 함수의 책임은 아니지만,이 함수의 반환 값 바로 앞에 인용되지 않은 백 슬래시가 나타나면 여전히 실패한다는 점에 주목하는 것이 흥미로울 것입니다. 사기 : 안전하다고 믿을 수있는 코드 (하드 코딩 된 명령의 일부 등)에서 이것을 사용해야합니다. 인용되지 않은 다른 사용자 입력에 추가하지 마십시오.
lhunath

10
쉘 기능이 절대적으로 필요하지 않다면 Jamie의 제안을 대신 사용해야합니다.
lhunath

6
이와 유사한 것이 이제 공식적으로 shlex.quote로 제공됩니다 .
Janus Troelsen 2012 년

3
이 답변에 제공된 함수는 shlex또는 보다 더 나은 쉘 인용 작업을 수행 pipes합니다. 이러한 파이썬 모듈은 특수 문자가 인용되어야하는 유일한 것으로 잘못 가정합니다. 즉 time, 해당 동작이 예상되지 않을 때 쉘 키워드 (예 : case또는 while)가 구문 분석 된다는 의미입니다 . 그런 이유로 나는이 답변에서 작은 따옴표 루틴을 사용하는 것이 좋습니다. 왜냐하면 그것은 "영리한"것을 시도하지 않기 때문에 그런 어리석은 가장자리 사례가 없기 때문입니다.
user3035772

157

shlex.quote() 파이썬 3 이후로 원하는 것을 수행합니다.

( pipes.quotepython 2와 python 3을 모두 지원하는 데 사용 )


또한 commands.mkarg . 또한 바람직하지 않을 수도있는 선행 공백 (따옴표 바깥 쪽)을 추가합니다. 구현이 서로 매우 다르고 Greg Hewgill의 답변보다 훨씬 더 복잡하다는 점이 흥미 롭습니다.
Laurence Gonsalves

3
어떤 이유로 파이프 모듈에 대한 표준 라이브러리 문서에pipes.quote 언급되지 않았습니다.
Day

1
둘 다 문서화되지 않았습니다. command.mkarg3.x에서는 더 이상 사용되지 않고 제거되었지만 pipes.quote는 그대로 유지되었습니다.
Beni Cherniavsky-Paskin

9
수정 : shlex.quote()3.3에서 공식적으로 문서화되고 pipes.quote()호환성을 위해 유지됩니다. [ bugs.python.org/issue9723]
Beni Cherniavsky-Paskin 2011 년

7
파이프는 Windows에서 작동하지 않습니다-큰 따옴표로 묶인 작은 따옴표를 추가합니다.
Nux

58

.NET을 사용하는 특별한 이유가있을 수 있습니다 os.system(). 그러나 그렇지 않다면 아마도 모듈 을 사용해야 할 것입니다.subprocess . 파이프를 직접 지정하고 셸 사용을 피할 수 있습니다.

다음은 PEP324 에서 가져온 것입니다 .

Replacing shell pipe line
-------------------------

output=`dmesg | grep hda`
==>
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]

6
subprocess(특히 check_calletc)는 종종 극적으로 우수하지만 쉘 이스케이프가 여전히 유용한 경우가 몇 가지 있습니다. 내가 실행하는 주된 것은 ssh 원격 명령을 호출해야 할 때입니다.
크레이그 링거

@CraigRinger, yup, ssh remoting이 나를 여기로 가져온 것입니다. : PI는 ssh가 여기에 도움이되기를 바랍니다.
Jürgen A. Erhard

@ JürgenA.Erhard --execvp-remote 옵션이없는 것이 이상해 보입니다 (또는 기본적으로 그렇게 작동합니다). 셸을 통해 모든 것을 수행하는 것은 서투르고 위험 해 보입니다. OTOH, ssh는 이상한 기이함으로 가득 차 있으며, 종종 "보안"이라는 좁은 관점에서 수행되는 작업으로 인해 사람들이 훨씬 더 안전하지 않은 해결 방법을 제시하게됩니다.
Craig Ringer

10

어쩌면 subprocess.list2cmdline더 나은 샷일까요?


꽤 좋아 보인다. 흥미롭게도 문서화되지 않았습니다 ... ( 적어도 docs.python.org/library/subprocess.html 에 있음)
Tom

4
제대로 탈출하지 않습니다 \ : subprocess.list2cmdline(["'",'',"\\",'"'])제공' "" \ \"
Tino

쉘 확장 기호를 이스케이프하지 않습니다
grep

subprocess.list2cmdline ()은 Windows 전용입니까?
JS.

@JS 예, list2cmdlineWindows cmd.exe 구문을 따릅니다 ( Python 소스 코드의 docstring 함수 참조 ). shlex.quoteUnix bourne 쉘 구문을 따르지만 Unix는 인수를 직접 전달하는 데 좋은 지원을 제공하므로 일반적으로 필요하지 않습니다. Windows에서는 거의 모든 인수와 함께 단일 문자열을 전달해야합니다 (따라서 적절한 이스케이프가 필요함).
eestrada

7

pipes.quote는 실제로 Python 2.5 및 Python 3.1에서 깨졌고 사용하기에 안전하지 않습니다. 길이가 0 인 인수를 처리하지 않습니다.

>>> from pipes import quote
>>> args = ['arg1', '', 'arg3']
>>> print 'mycommand %s' % (' '.join(quote(arg) for arg in args))
mycommand arg1  arg3

Python issue 7476을 참조하십시오 . Python 2.6 및 3.2 이상에서 수정되었습니다.


4
어떤 버전의 Python을 사용하고 있습니까? mycommand ARG1 ''에서 arg3 (! 스택 오버플로 글꼴 하드 말할 것을하게하지만 그 두 개의 작은 따옴표가 함께 있습니다) : 버전 2.6은 올바른 출력을 생산하는 것
브랜든 로즈

4

주의 : 이것은 Python 2.7.x에 대한 답변입니다.

에 따르면 소스 , pipes.quote()"할 수있는 방법이다 안정적에 대해 하나의 인수로 문자열 인용 / 빈 / SH는 ". (단, 버전 2.7부터 사용되지 않고 마침내 Python 3.3에서 shlex.quote()함수 로 공개적으로 노출되었습니다 .)

다른 한편으로 , subprocess.list2cmdline()"할 수있는 방법 은 AS와 동일한 규칙을 사용하여 명령 행 문자열로 인수의 순서를 번역 MS C 런타임 ".

여기에 명령 줄에 대한 문자열을 인용하는 플랫폼 독립적 인 방법이 있습니다.

import sys
mswindows = (sys.platform == "win32")

if mswindows:
    from subprocess import list2cmdline
    quote_args = list2cmdline
else:
    # POSIX
    from pipes import quote

    def quote_args(seq):
        return ' '.join(quote(arg) for arg in seq)

용법:

# Quote a single argument
print quote_args(['my argument'])

# Quote multiple arguments
my_args = ['This', 'is', 'my arguments']
print quote_args(my_args)

3

나는 os.system이 사용자를 위해 구성된 명령 쉘을 호출한다고 믿기 때문에 플랫폼 독립적 인 방식으로 할 수 있다고 생각하지 않습니다. 내 명령 셸은 bash, emacs, ruby ​​또는 quake3의 모든 것이 될 수 있습니다. 이 프로그램 중 일부는 당신이 그들에게 전달하는 주장의 종류를 기대하지 않으며 그들이 그렇게하더라도 그들이 같은 방식으로 탈출한다는 보장이 없습니다.


2
대부분 또는 완전히 POSIX 호환 셸을 기대하는 것은 비합리적이지 않습니다 (적어도 Windows를 제외한 모든 곳에서 "셸"이 무엇인지 알고 있습니다). os.system은 적어도 여기에서는 $ SHELL을 사용하지 않습니다.

2

내가 사용하는 기능은 다음과 같습니다.

def quote_argument(argument):
    return '"%s"' % (
        argument
        .replace('\\', '\\\\')
        .replace('"', '\\"')
        .replace('$', '\\$')
        .replace('`', '\\`')
    )

즉, 인수를 항상 큰 따옴표로 묶은 다음 큰 따옴표 안에 특수 문자 만 백 슬래시로 묶습니다.


'\\ "', '\\ $'및 '\`'를
사용해야합니다

1
또한 일부 (이상한) 로케일에서 큰 따옴표를 사용하는 데 문제가 있습니다 . pipes.quote@JohnWiseman이 지적한 제안 수정 사용 도 중단되었습니다. 따라서 Greg Hewgill의 대답이 사용됩니다. (또한 껍질이 정규 경우에 내부적으로 사용하는 하나입니다.)
mirabilos

-3

시스템 명령을 사용하는 경우 os.system () 호출에 들어가는 것을 화이트리스트에 추가하려고합니다. 예를 들어 ..

clean_user_input re.sub("[^a-zA-Z]", "", user_input)
os.system("ls %s" % (clean_user_input))

하위 프로세스 모듈이 더 나은 옵션이며 가능하면 os.system / subprocess와 같은 것을 사용하지 않는 것이 좋습니다.


-3

진짜 대답은 : os.system()처음부터 사용하지 마십시오 . subprocess.call대신 사용 하고 이스케이프되지 않은 인수를 제공하십시오.


6
질문에는 하위 프로세스가 실패하는 예가 포함되어 있습니다. 하위 프로세스를 사용할 수 있다면 확실히해야합니다. 하지만 할 수 없다면 ... 하위 프로세스는 모든 것에 대한 해결책이 아닙니다 . 아, 그리고 당신의 대답은 질문에 전혀 대답하지 않습니다.
Jürgen A. Erhard 2013 년

@ JürgenA.Erhard가 쉘 파이프를 사용하기를 원하기 때문에 OP의 예제가 실패하지 않습니까? 쉘을 사용하지 않기 때문에 항상 subprocess를 사용해야합니다. 이것은 약간 서투른 예제 이지만 기본 하위 프로세스에서 파이프를 수행 할 수 있습니다.이 작업을 더 쉽게하려는 몇 가지 pypi 패키지가 있습니다. 나는 가능한 한 파이썬에서 필요한 포스트 프로세싱을하는 경향이있다. 당신은 항상 자신 만의 StringIO 버퍼를 만들고 서브 프로세스로 모든 것을 완벽하게 제어 할 수있다.
ThorSummoner
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.