Pourquoi ne puis-je pas utiliser '~' au lieu de '/ home / nom d'utilisateur /' lorsque je donne le chemin du fichier

43

Je peux utiliser ~au lieu de /home/username/pointer sur un chemin de fichier lorsque, par exemple, décompresser un .zipfichier.

Cependant, aujourd’hui, lorsque j’ai suivi la même procédure pour exécuter un exemple RNN en terminal, cela a tensorflow.python.framework.errors_impl.NotFoundErrorété jeté.

$ 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

Ensuite, j'ai remplacé ~par /home/username/, et cela a fonctionné correctement.

Pourquoi est-ce que je ne pouvais pas utiliser ~au lieu de /home/username/pointer sur le chemin du fichier lors de l'exécution d'un exemple RNN?

Pourriez-vous me dire en détail?

JNing
la source
@OskarSkog Le shell ne devrait-il pas développer le ~avant que l'argument ne soit passé à python? Tout comme le shell développerait les barres obliques inverses dans le chemin, ou supprimerait les guillemets si le chemin était cité.
Micheal Johnson
1
Contrairement à $VARIABLES, le ~n'est développé qu'au début d'une chaîne.
alexis
@OskarSkog, « Python ne sait pas ce que ~ signifie » implique qu'un problème est spécifique à Python manque un morceau de fonctionnalité, la mise en place d' une attente déraisonnable que cette fonctionnalité (d'effectuer l' expansion après être exec« d) devrait être largement disponible dans les outils UNIX .
Charles Duffy

Réponses:

45

Vous devez comprendre que cela ~est normalement développé par le shell; les programmes que vous appelez ne le voient jamais, ils voient le nom de chemin complet inséré par bash. Mais cela ne se produit que lorsque le tilde est au début d'un argument (et n'est pas cité).

Si le programme Python que vous exécutez utilise un module similaire getoptà celui de l'analyse de la ligne de commande, vous pouvez définir l'argument de l' --data-pathoption sous la forme d'un "mot" distinct pour permettre le développement du tilde:

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

Dans votre propre code, vous pouvez utiliser getoptou argparsepour le traitement des arguments, et également développer manuellement les tildes, comme le suggère la réponse de @ JacobVlijm.

PS Le tilde est également développé au début d’une expression d’affectation de variable shell telle que DIRNAME=~/anaconda2; bien que le tilde de votre question suive également un signe égal, cet usage n'a pas de signification particulière pour le shell (il s'agit simplement d'un élément transmis à un programme) et ne déclenche pas d'expansion.

alexis
la source
6
À moins que vous ne le sachiez getopt déjà, utilisez-le argparsesi vous écrivez en Python.
Nick T
J'ai ajouté argparseà la réponse car c'est l'alternative principale, mais personnellement, je trouve qu'il est beaucoup plus difficile à utiliser que getopt, pas plus facile. YMMV.
alexis
33

Expansion de tilde en python

La réponse est courte et simple:

Python ne se développe pas ~sauf si vous utilisez:

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

Voir aussi ici :

os.path.expanduser (path)
Sous Unix et Windows, renvoyez l'argument avec un composant initial de ~ ou ~ user remplacé par le répertoire de base de cet utilisateur.

Sous Unix, une initiale ~ est remplacée par la variable d’environnement HOME si elle est définie; sinon, le répertoire de base de l'utilisateur actuel est recherché dans le répertoire du mot de passe via le module intégré pwd. Un ~ utilisateur initial est recherché directement dans le répertoire du mot de passe.

Jacob Vlijm
la source
11
En général, vous ne devriez jamais supposer que l'extension du tilde se fait au niveau du système d'exploitation, c'est quelque chose que les shells unix (et pas tous!) Font pour vous.
farsil
1
Je pense que la question la plus pertinente est soulignée dans la réponse d'Alexis: la position de ~dans la liste d'arguments shell.
David Foerster
@farsil, je ne suis pas d'accord. Les programmes peuvent être rendus portables, mais lorsque vous les exécutez à partir de la ligne de commande, vous le faites sur un système spécifique. Et n'oublions pas que c'est askubuntu.com, et Ubuntu est toujours Unix ( à notre connaissance :-)
alexis
1
@ alexis: Ubuntu ne fait pas non plus l'expansion du tilde au niveau du système d'exploitation. C'est toujours la fonctionnalité du shell.
user2357112 prend en charge Monica
1
Il me semble que vous coupez les cheveux en quatre. Personne n'a dit que le noyau le faisait. Le fait est que ce n'est pas fait par le programme qui prend les arguments.
alexis
12

L'expansion de tilde n'est effectuée que dans quelques contextes qui varient légèrement d'un shell à l'autre .

Bien qu'il soit exécuté en:

var=~

Ou

export var=~

dans certains coquillages. Ce n'est pas dans

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

dans des coquilles POSIX.

Il est dans bashcependant si pas en mode de conformité POSIX (comme quand appelé sh, ou quand POSIXLY_CORRECTest dans l'environnement):

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

Quoi qu'il en est seulement quand ce qui est à gauche de l' =est en forme comme un unquoted nom de variable valide, si bien qu'il serait élargi en cmd prefix=~, il ne serait pas cmd --prefix=~(comme --prefixn'est pas un nom de variable valide) ni cmd "p"refix=~( à cause de cette cité p) , ni dans var=prefix; cmd $var=~.

Dans zsh, vous pouvez définir l' magic_equal_substoption ~à développer après tout non cité =.

$ 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

Dans le cas de ~(par opposition à ~user), vous pouvez simplement utiliser $HOME:

cmd --whatever="$HOME/whatever"

~se développe à la valeur de $HOME. Si $HOMEn'est pas défini, le comportement varie d'un shell à l'autre. Certains shells interrogent la base de données utilisateur. Si vous voulez en tenir compte, vous pouvez faire (et c'est aussi ce que vous devez faire ~user):

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

Dans tous les cas, dans les coques autres que celles zshutilisées, rappelez-vous qu'il vous faut citer des extensions variables!

Stéphane Chazelas
la source
1
Le manuel de référence de Bash semble indiquer que les textures ne sont développées que sur des affectations variables et au début d'un mot, aussi son expansion echo a=~semble contredire le manuel.
ilkkachu
@ilkkachu, oui le manuel est incomplet. Il ne précise pas non plus clairement dans quel contexte ~sera développé (ce que l’on entend par "mot"). Voir le lien en haut de la réponse pour plus de détails.
Stéphane Chazelas
6

~a des règles d'expansion particulières, auxquelles votre commande ne satisfait pas. Plus précisément, il est développé uniquement lorsqu'il est non mis entre guillemets, soit au début d'un mot (par exemple python ~/script.py), soit au début d'une affectation de variable (par exemple PYTHONPATH=~/scripts python script.py). Ce que vous avez est --data_path=~/blablace qui est un mot simple en termes de shell, de sorte que l'expansion n'est pas effectuée.

Une solution immédiate consiste à utiliser une $HOMEvariable shell, qui suit les règles habituelles de développement des variables:

python ptb_word_lm.py --data_path=$HOME/blabla
Dmitry Grigoryev
la source
C’est un peu trop simpliste, il ya d’autres contextes dans lesquels l’extension de tilde est réalisée comme dans PATH=$PATH:~/bin. De plus, il $HOMEfaut citer ou scinder + glob s'applique dans les shells autres que zsh.
Stéphane Chazelas
@sch désolé, mais le lien que vous avez fourni dans le commentaire conduit à une question sur la souris optique, sans mention de l'expansion du tilde. Pouvez-vous s'il vous plaît expliquer cela?
Commentaires
Bonne réponse. Il résume en gros ce que dit le bashmanuel dans la Tilde Expansionsection. +1
Sergiy Kolodyazhnyy
Désolé, je suis tellement habitué à utiliser des liens intra-site dans unix.SE car [link](/a/146697)je ne savais pas que nous étions sur un site différent ici. Le lien devrait être
Stéphane Chazelas