Domanda

Quando si utilizza os.system() è spesso necessario eseguire l'escape dei nomi dei file e di altri argomenti passati come parametri ai comandi.Come posso fare questo?Preferibilmente qualcosa che funzioni su più sistemi operativi/shell ma in particolare per bash.

Attualmente sto facendo quanto segue, ma sono sicuro che ci deve essere una funzione di libreria per questo, o almeno un'opzione più elegante/robusta/efficiente:

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)))

Modificare: Ho accettato la semplice risposta di usare le virgolette, non so perché non ci ho pensato;Immagino perché provengo da Windows dove ' e " si comportano in modo leggermente diverso.

Per quanto riguarda la sicurezza, capisco la preoccupazione, ma, in questo caso, sono interessato a una soluzione semplice e veloce fornita da os.system() e l'origine delle stringhe non è generata dall'utente o almeno inserita da un utente fidato (io).

È stato utile?

Soluzione

Questo è quello che uso:

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

La shell accetterà sempre un nome file tra virgolette e rimuoverà le virgolette circostanti prima di passarlo al programma in questione.In particolare, questo evita problemi con nomi di file che contengono spazi o qualsiasi altro tipo di brutto metacarattere della shell.

Aggiornamento:Se stai usando Python 3.3 o successivo, usa shlex.quote invece di rotolare il tuo.

Altri suggerimenti

shlex.quote() fa quello che vuoi da Python 3.

(Utilizzo pipes.quote per supportare sia Python 2 che Python 3)

Forse hai un motivo specifico per l'utilizzo os.system().Ma in caso contrario probabilmente dovresti usare il file subprocess modulo.È possibile specificare direttamente le pipe ed evitare di utilizzare la shell.

Quello che segue è da 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]

Forse subprocess.list2cmdline è uno scatto migliore?

Tieni presente che pipe.quote in realtà non funziona in Python 2.5 e Python 3.1 e non è sicuro da usare: non gestisce argomenti di lunghezza zero.

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

Vedere Problema di Python 7476;è stato corretto in Python 2.6 e 3.2 e versioni successive.

Credo che os.system invochi semplicemente qualunque shell di comando sia configurata per l'utente, quindi non penso che tu possa farlo in modo indipendente dalla piattaforma.La mia shell di comando potrebbe essere qualsiasi cosa, da bash, emacs, ruby ​​o anche quake3.Alcuni di questi programmi non si aspettano il tipo di argomenti che stai passando loro e anche se lo facessero non c'è alcuna garanzia che escano allo stesso modo.

Avviso:Questa è una risposta per Python 2.7.x.

Secondo il fonte, pipes.quote() è un modo per"Cita in modo affidabile una stringa come singolo argomento per /bin/sh".(Sebbene sia deprecato dalla versione 2.7 e infine esposto pubblicamente in Python 3.3 come shlex.quote() funzione.)

SU l'altra mano, subprocess.list2cmdline() è un modo per"Traduci una sequenza di argomenti in una stringa della riga di comando, utilizzando le stesse regole del file Tempo di esecuzione di MS C".

Eccoci qui, il modo indipendente dalla piattaforma di citare le stringhe per le righe di comando.

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)

Utilizzo:

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

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

La funzione che utilizzo è:

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

questo è:Racchiudo sempre l'argomento tra virgolette doppie, quindi virgolette con barra rovesciata gli unici caratteri speciali racchiusi tra virgolette doppie.

Se usi il comando di sistema, proverei a inserire nella whitelist ciò che va nella chiamata os.system().Per esempio..

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

Il modulo subprocess è un'opzione migliore e consiglierei di provare a evitare di utilizzare qualcosa come os.system/subprocess ove possibile.

La vera risposta è:Non utilizzare os.system() innanzitutto.Utilizzo subprocess.call invece e fornire gli argomenti senza escape.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top