Warum konnte ich bei der Angabe des Dateipfads nicht '~' anstelle von '/ home / username /' verwenden?

43

Ich kann ~anstelle von /home/username/auf einen Dateipfad verweisen, wenn ich zum Beispiel eine .zipDatei entpacke .

Als ich jedoch heute den gleichen Weg ging, um ein RNN-Beispiel im Terminal auszuführen, tensorflow.python.framework.errors_impl.NotFoundErrorwurde geworfen.

$ python ptb_word_lm.py --data_path=~/anaconda2/lib/python2.7/site-packages/tensorflow/models-master/tutorials/rnn/simple-examples/data/ --model=small 
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcublas.so.8.0 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcudnn.so.5 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcufft.so.8.0 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcuda.so.1 locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcurand.so.8.0 locally
Traceback (most recent call last):
  File "ptb_word_lm.py", line 374, in <module>
    tf.app.run()
  File "/home/hok/anaconda2/lib/python2.7/site-packages/tensorflow/python/platform/app.py", line 44, in run
    _sys.exit(main(_sys.argv[:1] + flags_passthrough))
  File "ptb_word_lm.py", line 321, in main
    raw_data = reader.ptb_raw_data(FLAGS.data_path)
  File "/home/hok/anaconda2/lib/python2.7/site-packages/tensorflow/models-master/tutorials/rnn/ptb/reader.py", line 73, in ptb_raw_data
    word_to_id = _build_vocab(train_path)
  File "/home/hok/anaconda2/lib/python2.7/site-packages/tensorflow/models-master/tutorials/rnn/ptb/reader.py", line 34, in _build_vocab
    data = _read_words(filename)
  File "/home/hok/anaconda2/lib/python2.7/site-packages/tensorflow/models-master/tutorials/rnn/ptb/reader.py", line 30, in _read_words
    return f.read().decode("utf-8").replace("\n", "<eos>").split()
  File "/home/hok/anaconda2/lib/python2.7/site-packages/tensorflow/python/lib/io/file_io.py", line 106, in read
    self._preread_check()
  File "/home/hok/anaconda2/lib/python2.7/site-packages/tensorflow/python/lib/io/file_io.py", line 73, in _preread_check
    compat.as_bytes(self.__name), 1024 * 512, status)
  File "/home/hok/anaconda2/lib/python2.7/contextlib.py", line 24, in __exit__
    self.gen.next()
  File "/home/hok/anaconda2/lib/python2.7/site-packages/tensorflow/python/framework/errors_impl.py", line 469, in raise_exception_on_not_ok_status
    pywrap_tensorflow.TF_GetCode(status))
tensorflow.python.framework.errors_impl.NotFoundError: ~/anaconda2/lib/python2.7/site-packages/tensorflow/models-master/tutorials/rnn/simple-examples/data/ptb.train.txt

Dann ersetzte ich ~mit /home/username/, und es funktionierte einwandfrei .

Warum konnte ich beim Ausführen eines RNN-Beispiels nicht auf den Dateipfad verweisen, ~anstatt ihn /home/username/zu verwenden?

Könntest du mir das genauer sagen?

JNing
quelle
Ist ~ immer gleich $ HOME
Stéphane Chazelas
@OskarSkog Sollte die Shell das nicht erweitern, ~bevor das Argument an Python übergeben wird? Genau wie die Shell Backslash-Fluchten im Pfad erweitern oder Anführungszeichen entfernen würde, wenn der Pfad in Anführungszeichen gesetzt wurde.
Micheal Johnson
1
Im Gegensatz dazu $VARIABLESwird das ~nur am Anfang einer Zeichenkette erweitert.
Alexis
@OskarSkog, "Python weiß nicht, was ~ bedeutet" impliziert, dass ein Problem für Python spezifisch ist, dem ein Teil der Funktionalität fehlt, was zu der unzumutbaren Erwartung führt, dass diese Funktionalität (die Erweiterung nach dem Ausführen exec) in UNIX-Tools allgemein verfügbar sein sollte .
Charles Duffy

Antworten:

45

Sie müssen verstehen, dass dies ~normalerweise durch die Shell erweitert wird. Die Programme, die Sie aufrufen, sehen es nie. Sie sehen den vollständigen Pfadnamen, wie er von der Bash eingefügt wurde. Dies geschieht jedoch nur, wenn die Tilde am Anfang eines Arguments steht (und nicht in Anführungszeichen steht).

Wenn das von Ihnen ausgeführte Python-Programm ein Modul getoptzum Parsen der Befehlszeile verwendet, können Sie das Argument der --data-pathOption als separates "Wort" angeben, um die Tilde-Erweiterung zu ermöglichen:

$ python ptb_word_lm.py --data_path ~/anaconda2/lib/python2.7/...

In Ihrem eigenen Code können Sie getoptoder argparsefür die Argumentverarbeitung verwenden und Tildes auch manuell erweitern, wie in der Antwort von @ JacobVlijm vorgeschlagen.

PS. Die Tilde ist auch zu Beginn eines erweiterten Schalen variable Zuweisungsausdruck wie DIRNAME=~/anaconda2; Obwohl die Tilde in Ihrer Frage auch einem Gleichheitszeichen folgt, hat diese Verwendung keine besondere Bedeutung für die Shell (es ist nur etwas, das an ein Programm übergeben wird) und löst keine Erweiterung aus.

alexis
quelle
6
Wenn Sie es noch nicht wissen getopt , verwenden argparseSie , wenn Sie Python schreiben.
Nick T
Ich habe argparsedie Antwort ergänzt, da es die Hauptalternative ist, aber ich persönlich finde es viel schwieriger zu bedienen als getopt, nicht einfacher. YMMV.
Alexis
33

Tilde-Erweiterung in Python

Die Antwort ist kurz und einfach:

Python wird nur dann erweitert, ~wenn Sie Folgendes verwenden:

import os
os.path.expanduser('~/your_directory')

Siehe auch hier :

os.path.expanduser (path)
Unter Unix und Windows geben Sie das Argument mit einer Anfangskomponente von ~ oder ~ user zurück, die durch das Ausgangsverzeichnis dieses Benutzers ersetzt wird.

Unter Unix wird ein initiales ~ durch die Umgebungsvariable HOME ersetzt, wenn diese gesetzt ist. Andernfalls wird das Ausgangsverzeichnis des aktuellen Benutzers über das integrierte Modul pwd im Kennwortverzeichnis nachgeschlagen. Ein initialer Benutzer wird direkt im Passwortverzeichnis nachgeschlagen.

Jacob Vlijm
quelle
11
Im Allgemeinen sollten Sie niemals davon ausgehen, dass die Tilde-Erweiterung auf Betriebssystemebene erfolgt. Dies tun Unix-Shells (und nicht alle!) Für Sie.
Farsil
1
Ich denke, das relevantere Thema ist in der Antwort von alexis aufgeführt: die Position von ~in der Shell-Argumentliste.
David Foerster
@ Farsil, ich bin anderer Meinung. Programme können portabel gemacht werden, aber wenn Sie sie über die Befehlszeile ausführen, tun Sie dies auf einem bestimmten System. Und vergessen wir nicht, dass dies askubuntu.com ist und Ubuntu immer Unix ist ( soweit wir wissen :-)
alexis
1
@alexis: Ubuntu führt auch auf Betriebssystemebene keine Tilde-Erweiterung durch. Es ist immer noch Shell-Funktionalität.
user2357112
1
Glaubst du, du spaltest Haare? Niemand hat gesagt, dass der Kernel das tut. Der Punkt ist, es wird nicht von dem Programm gemacht, das die Argumente übernimmt.
Alexis
12

Die Tilde-Erweiterung wird nur in wenigen Kontexten durchgeführt, die sich zwischen den Shells geringfügig unterscheiden .

Während es in durchgeführt wird:

var=~

Oder

export var=~

in einigen Muscheln. Es ist nicht in

echo var=~
env var=~ cmd
./configure --prefix=~

in POSIX-Shells.

Es ist jedoch in, bashwenn nicht im POSIX-Konformitätsmodus (wie beim Aufrufen als shoder wenn POSIXLY_CORRECTin der Umgebung):

$ bash -c 'echo a=~'
a=/home/stephane
$ POSIXLY_CORRECT= bash -c 'echo a=~'
a=~
$ SHELLOPTS=posix bash -c 'echo a=~'
a=~
$ (exec -a sh bash -c 'echo a=~')
a=~

Dies ist jedoch nur dann der =Fall cmd prefix=~, wenn das, was auf der linken Seite der steht, wie ein nicht zitierter gültiger Variablenname geformt ist. Während es also in erweitert würde, würde es weder in cmd --prefix=~(wie --prefixes kein gültiger Variablenname ist) noch in cmd "p"refix=~(aus diesem Grund p) noch sein in var=prefix; cmd $var=~.

In zshkönnen Sie festlegen, dass die magic_equal_substOption ~nach einem nicht zitierten Eintrag erweitert werden soll =.

$ zsh -c 'echo a=~'
a=~
$ zsh -o magic_equal_subst -c 'echo a=~'
a=/home/stephane
$ zsh -o magic_equal_subst -c 'echo --a=~'
--a=/home/stephane

Im Fall von ~(im Gegensatz zu ~user) können Sie $HOMEstattdessen einfach Folgendes verwenden:

cmd --whatever="$HOME/whatever"

~erweitert auf den Wert von $HOME. Wenn $HOMEnicht festgelegt, variiert das Verhalten zwischen den Shells. Einige Shells fragen die Benutzerdatenbank ab. Wenn Sie das berücksichtigen möchten, können Sie Folgendes tun (und dafür müssten Sie auch Folgendes tun ~user):

dir=~ # or dir=~user
cmd --whatever="$dir/whatever"

In jedem Fall müssen Sie in anderen Shells als " zshDenken Sie daran" variable Erweiterungen angeben!

Stéphane Chazelas
quelle
1
Bashs Referenzhandbuch scheint zu sagen, dass Tildes nur bei variablen Zuweisungen und am Anfang eines Wortes erweitert werden, so dass eine Erweiterung echo a=~des Handbuchs dem Handbuch zu widersprechen scheint.
ilkkachu
@ilkkachu, ja das Handbuch ist unvollständig. Es wird auch nicht klar angegeben, in welchem ​​Kontext ~erweitert wird (was mit "Wort" gemeint ist). Weitere Informationen finden Sie unter dem Link oben in der Antwort.
Stéphane Chazelas
6

~hat bestimmte Erweiterungsregeln, die Ihr Befehl nicht erfüllt. Insbesondere wird es nur erweitert, wenn es nicht in Anführungszeichen gesetzt ist, entweder am Anfang eines Wortes (z. B. python ~/script.py) oder am Anfang einer variablen Zuweisung (z PYTHONPATH=~/scripts python script.py. B. ). Was Sie haben --data_path=~/blabla, ist ein einzelnes Wort in Shell-Begriffen, so dass keine Erweiterung durchgeführt wird.

Eine sofortige Lösung besteht darin, eine $HOMEShell-Variable zu verwenden, die den regulären Regeln für die Variablenerweiterung folgt:

python ptb_word_lm.py --data_path=$HOME/blabla
Dmitry Grigoryev
quelle
Das ist etwas vereinfacht, es gibt andere Kontexte, in denen die Tilde-Erweiterung wie in ausgeführt wird PATH=$PATH:~/bin. Auch das $HOMEmuss in Anführungszeichen gesetzt werden oder split + glob gilt in anderen Shells als zsh.
Stéphane Chazelas
@sch sorry, aber der Link, den du in dem Kommentar dort angegeben hast, führt zu einer Frage zur optischen Maus, ohne dass die Tilde-Erweiterung erwähnt wird. Kannst du das bitte erklären?
Sergiy Kolodyazhnyy
Gute Antwort. Es fasst grundsätzlich zusammen, welche bashmanuellen Zustände in dem Tilde ExpansionAbschnitt vorliegen. +1
Sergiy Kolodyazhnyy
Tut mir leid, ich bin es so gewohnt, Links innerhalb einer Site in unix.SE zu verwenden, [link](/a/146697)dass ich nicht realisiert habe, dass wir hier auf einer anderen Site waren. Der Link sollte dort sein
Stéphane Chazelas