[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