[Python-de] NULL-Zeichen in Kommandozeilenargumenten, wie (wenn überhaupt)?

Dinu Gherman gherman at darwin.in-berlin.de
Mi Jan 2 22:47:29 CET 2013


Hallo und ein Gutes Neues!

Ich bastle an einem Tool, das in der Kommandozeile möglicherweise viele Dateipfade als Argumente bekommen kann. Dieses kann man dann z.B. wie folgt aufrufen (statt find könnte es auch locate oder auf OS X mdfind sein):

$ tool.py $(find . -name "*.tgz")
$ find . -name "*.tgz" | xargs tool.py

Nun dürfen diese Pfade auch Leerzeichen enthalten. Für solche Fälle können diese Suchtools mit find -print0, locate -0 oder mdfind -0 die Liste der gefundenen Pfade statt mit Zeilenumbrüchen mit ASCII-NULL-Zeichen trennen. Diese kann xargs -0 dann wieder auseinandernehmen, um dann ein- oder mehrmals tool.py aufzurufen. Hier ein kleines Beispiel:

$ ls -l
drwxr-xr-x  5 dinu  staff   170  2 Jan 17:47 sample/
-rwxr-xr-x  1 dinu  staff   278  2 Jan 19:32 tool.py

$ ls -l sample
total 24
-rw-r--r--  1 dinu  staff   4  2 Jan 17:45 bar.txt
-rw-r--r--  1 dinu  staff   4  2 Jan 17:45 foo.txt
-rw-r--r--  1 dinu  staff  10  2 Jan 17:47 foot bart.txt

$ find sample -name "*.txt"
sample/bar.txt
sample/foo.txt
sample/foot bart.txt

Mein (stark reduziertes) Tool macht nun nichts anderes, als seine Kommandozeilenargumente auszugeben:

$ more tool.py
#!/usr/bin/env python
import sys
argv = sys.argv[:]
print "NULL found in args:", chr(0) in ("".join(argv))
print "args:", argv
for a in argv[1:]:
    print "arg:", a

Erwartungsgemäß funktioniert das bei Leerzeichen im Dateinamen nicht wie gewünscht:

$ ./tool.py $(find sample -name "*.txt")
NULL found in args: False
args: ['./tool.py', 'sample/bar.txt', 'sample/foo.txt', 'sample/foot', 'bart.txt']
arg: sample/bar.txt
arg: sample/foo.txt
arg: sample/foot
arg: bart.txt

$ find sample -name "*.txt" | xargs ./tool.py
NULL found in args: False
args: ['./tool.py', 'sample/bar.txt', 'sample/foo.txt', 'sample/foot', 'bart.txt']
arg: sample/bar.txt
arg: sample/foo.txt
arg: sample/foot
arg: bart.txt

Außer, man trennt die Pfade mit NULLen, wenn man das find und xargs mitteilt:

$ find sample -name "*.txt" -print0 | xargs -0 ./tool.py
NULL found in args: False
args: ['./tool.py', 'sample/bar.txt', 'sample/foo.txt', 'sample/foot bart.txt']
arg: sample/bar.txt
arg: sample/foo.txt
arg: sample/foot bart.txt

Ohne das xargs -0 liefert find, was zu erwarten ist (das more macht nur die NULLen im Terminal sichtbar):

$ find sample -name "*.txt" -print0 | more
sample/bar.txt^@sample/foo.txt^@sample/foot bart.txt^@

Mit xargs -0 passiert das, was passieren soll (die NULLen werden von xargs aufgelöst:

$ find sample -print0 | xargs -0 ./tool.py
NULL found in args: False
args: ['./tool.py', 'sample', 'sample/bar.txt', 'sample/foo.txt', 'sample/foot bart.txt']
arg: sample
arg: sample/bar.txt
arg: sample/foo.txt
arg: sample/foot bart.txt

Aber, und jetzt wird es spannend, wenn man versucht, die Funktion von xargs -0, also die Erkennung auf Aufteilung an den NULLen in tool.py selbst zu machen, dann stellt man fest, dass in sys.argv gar keine NULLen ankommen, sondern dort in diesem Beispiel zwei durch ein Leerzeichen getrennte Argumente stehen, als wären die NULLen von find gar nicht ausgegeben worden:

$ ./tool.py $(find sample -print0)
NULL found in args: False
args: ['./tool.py', 'samplesample/bar.txtsample/foo.txtsample/foot', 'bart.txt']
arg: samplesample/bar.txtsample/foo.txtsample/foot
arg: bart.txt

Kann das jemand erklären? Werden die NULLen in den Kommandozeilenargumenten von Python herausgefiltert? Wenn ja, warum? Kurz, warum verschwindet diese NULL:

$ python -c "import sys; print sys.argv" $(python -c "print 'abc' + chr(0) + 'xyz'")
['-c', 'abcxyz']

Dieses Verhalten ist übrigens für Python 2.7.3 und 3.3.0 identisch (getestet auf Mac OS X).

Danke/Gruß,

Dinu



Mehr Informationen über die Mailingliste python-de