¿Por qué no podría usar '~' en lugar de '/ home / username /' al dar la ruta del archivo?

43

Puedo usar en ~lugar de /home/username/señalar una ruta de archivo cuando, por ejemplo, descomprima un .ziparchivo.

Sin embargo, hoy, cuando seguí el mismo camino para ejecutar un ejemplo RNN en la terminal, me tensorflow.python.framework.errors_impl.NotFoundErrorarrojaron.

$ 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

Luego reemplacé ~con /home/username/, y funcionó correctamente.

¿Por qué no podría usar en ~lugar de /home/username/señalar la ruta del archivo al ejecutar un ejemplo RNN?

¿Podrías decirme en detalle?

JNing
fuente
¿~ Siempre es igual a $ HOME
Stéphane Chazelas
@OskarSkog ¿No debería el shell expandir ~antes de pasar el argumento a Python? Al igual que el shell expandiría los escapes de barra invertida en la ruta, o eliminaría las comillas si la ruta fuera entrecomillada.
Micheal Johnson
1
A diferencia $VARIABLES, el ~solo se expande al comienzo de una cadena.
alexis
@OskarSkog, "Python no sabe lo que ~ significa" implica que un problema es específico de Python que carece de una funcionalidad, estableciendo una expectativa irracional de que dicha funcionalidad (de realizar la expansión después de ser exec'd) debería estar ampliamente disponible en las herramientas UNIX .
Charles Duffy

Respuestas:

45

Debe comprender que ~normalmente se expande por el shell; los programas a los que llama nunca lo ven, ven el nombre de ruta completo como insertado por bash. Pero esto solo sucede cuando la tilde está al comienzo de un argumento (y no se cita).

Si el programa Python que está ejecutando usa un módulo como getoptanalizar su línea de comandos, puede dar el argumento de la --data-pathopción como una "palabra" separada para permitir la expansión de tilde:

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

En su propio código, puede utilizar getopto argparsepara el procesamiento de argumento, y también se puede ampliar de forma manual tildes como respuesta de @ JacobVlijm sugerido.

PD. La tilde también se expande al comienzo de una expresión de asignación de variable de shell como DIRNAME=~/anaconda2; Aunque la tilde en su pregunta también sigue un signo de igual, este uso no tiene un significado especial para el shell (es solo algo que se pasa a un programa) y no desencadena la expansión.

alexis
fuente
66
A menos que ya lo sepas getopt , úsalo argparsesi estás escribiendo Python.
Nick T
He agregado argparsela respuesta ya que es la alternativa principal, pero personalmente me resulta mucho más difícil de usar que getopt, no es más fácil. YMMV.
alexis
33

Expansión de Tilde en Python

La respuesta es corta y simple:

Python no se expande a ~menos que use:

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

Ver también aquí :

os.path.expanduser (ruta)
En Unix y Windows, devuelva el argumento con un componente inicial de ~ o ~ usuario reemplazado por el directorio de inicio de ese usuario.

En Unix, un ~ inicial se reemplaza por la variable de entorno HOME si está configurado; de lo contrario, el directorio de inicio del usuario actual se busca en el directorio de contraseñas a través del módulo incorporado pwd. Se busca un usuario ~ inicial directamente en el directorio de contraseñas.

Jacob Vlijm
fuente
11
En general, nunca debe suponer que la expansión de tilde se realiza a nivel de sistema operativo, es algo que los shells de Unix (¡y no todos!) Hacen por usted.
farsil
1
Creo que el tema más relevante se destaca en la respuesta de alexis: la posición de ~en la lista de argumentos de shell.
David Foerster
@farsil, no estoy de acuerdo. Los programas pueden hacerse portátiles, pero cuando los ejecuta desde la línea de comandos, lo hace en un sistema específico. Y no olvidemos que esto es askubuntu.com, y Ubuntu siempre es Unix ( hasta donde sabemos :-)
alexis
1
@alexis: Ubuntu tampoco hace la expansión tilde a nivel del sistema operativo. Sigue siendo la funcionalidad de shell.
user2357112 es compatible con Monica
1
Creo que te estás partiendo el pelo. Nadie dijo que el núcleo lo está haciendo. El punto es que no lo hace el programa que toma los argumentos.
alexis
12

La expansión de Tilde solo se realiza en unos pocos contextos que varían ligeramente entre conchas .

Mientras se realiza en:

var=~

O

export var=~

En algunas conchas. No esta en

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

en conchas POSIX.

Sin bashembargo, es cuando no está en modo de conformidad POSIX (como cuando se llama como sho cuando POSIXLY_CORRECTestá en el entorno):

$ 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=~

Sin embargo, eso es solo cuando lo que está a la izquierda del =tiene la forma de un nombre de variable válido sin comillas, por lo que si bien se expandiría en cmd prefix=~, no estaría en cmd --prefix=~(como --prefixno es un nombre de variable válido) ni en cmd "p"refix=~(debido a eso citado p) ni en var=prefix; cmd $var=~.

En zsh, puede configurar la magic_equal_substopción para ~que se expanda después de cualquier sin comillas =.

$ 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

En el caso de ~(en lugar de ~user), puede usar $HOMEen su lugar:

cmd --whatever="$HOME/whatever"

~se expande al valor de $HOME. Si $HOMEno se establece, el comportamiento varía entre los depósitos. Algunos shells consultan la base de datos del usuario. Si desea tener eso en cuenta, puede hacer (y eso es también lo que tendría que hacer ~user):

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

En cualquier caso, en otros shells que no sean zshrecordados, ¡necesita cotizar expansiones variables!

Stéphane Chazelas
fuente
1
El manual de referencia de Bash parece decir que las tildes se expanden solo en asignaciones variables y al comienzo de una palabra, por lo que expandirlo echo a=~parece contradecir el manual.
ilkkachu
@ilkkachu, sí, el manual está incompleto. Tampoco especifica claramente en qué contexto ~se expandirá (qué se entiende por "palabra"). Vea el enlace en la parte superior de la respuesta para más detalles.
Stéphane Chazelas
6

~tiene reglas de expansión particulares, que su comando no satisface. Específicamente, se expande solo cuando no se cita, ya sea al comienzo de una palabra (por ejemplo python ~/script.py) o al comienzo de una asignación variable (por ejemplo PYTHONPATH=~/scripts python script.py). Lo que tiene es --data_path=~/blablauna sola palabra en términos de shell, por lo que no se realiza la expansión.

Una solución inmediata es usar la $HOMEvariable de shell, que sigue las reglas regulares de expansión de variables:

python ptb_word_lm.py --data_path=$HOME/blabla
Dmitry Grigoryev
fuente
Eso está un poco simplificado, hay otros contextos en los que la expansión de tilde se realiza como en PATH=$PATH:~/bin. También eso $HOMEnecesita ser citado o split + glob se aplica en conchas que no sean zsh.
Stéphane Chazelas
@sch lo siento, pero el enlace que proporcionó en el comentario allí lleva a una pregunta sobre el mouse óptico, sin mencionar la expansión de tilde. ¿Puedes explicar eso por favor?
Sergiy Kolodyazhnyy
Buena respuesta. Resume básicamente lo bashque dice el manual en la Tilde Expansionsección. +1
Sergiy Kolodyazhnyy
Lo siento, estoy tan acostumbrado a usar enlaces dentro del sitio en Unix.SE [link](/a/146697)que no me di cuenta de que estábamos en un sitio diferente aquí. El enlace debería estar allí
Stéphane Chazelas