Por que não consegui usar '~' em vez de '/ home / username /' ao fornecer o caminho do arquivo

43

Posso usar em ~vez de /home/username/apontar para um caminho de arquivo quando, por exemplo, descompactar um .ziparquivo.

No entanto, hoje, quando segui o mesmo caminho para executar um exemplo da RNN no terminal, tensorflow.python.framework.errors_impl.NotFoundErrorfoi lançado.

$ 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

Então eu substituí ~por /home/username/, e funcionou corretamente.

Por que eu não poderia usar ~, em vez de /home/username/para apontar para o caminho do arquivo quando runing um exemplo RNN?

Você poderia me contar em detalhes?

JNing
fonte
Faz ~ sempre igual $ HOME
Stéphane Chazelas
@OskarSkog O shell não deveria expandir ~antes de o argumento ser passado para python? Assim como o shell expandiria escapes de barra invertida no caminho ou removeria aspas se o caminho fosse citado.
Micheal Johnson
1
Diferentemente $VARIABLES, o ~somente é expandido no início de uma string.
21478 alexis
@OskarSkog "Python não sabe o que ~ significa" implica que um problema é específico para Python faltando um pedaço de funcionalidade, a criação de uma expectativa razoável que tal funcionalidade (de realizar a expansão após ser exec'd) deve estar amplamente disponível em ferramentas UNIX .
Charles Duffy

Respostas:

45

Você precisa entender que ~normalmente é expandido pelo shell; os programas que você chama nunca o veem, eles veem o nome do caminho completo conforme inserido pelo bash. Mas isso só acontece quando o til está no início de um argumento (e não é citado).

Se o programa Python que você está executando usa um módulo como getoptpara analisar sua linha de comando, você pode fornecer o argumento da --data-pathopção como uma "palavra" separada para permitir a expansão do til:

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

No seu próprio código, você pode usar getoptou argparsepara o processamento de argumentos e também pode expandir manualmente os tildes, como sugeriu a resposta de @ JacobVlijm.

PS. O til também é expandido no início de uma expressão de atribuição de variável do shell como DIRNAME=~/anaconda2; embora o til na sua pergunta também siga um sinal de igual, esse uso não tem um significado especial para o shell (é apenas algo passado para um programa) e não aciona a expansão.

alexis
fonte
6
A menos que você já saiba getopt , use argparsese estiver escrevendo Python.
Nick T
Eu adicionei argparsea resposta, já que é a principal alternativa, mas pessoalmente acho muito mais difícil de usar do que getoptnão é mais fácil. YMMV.
21417 alexis
33

Expansão til em python

A resposta é curta e simples:

python não se expande a ~menos que você use:

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

Veja também aqui :

os.path.expanduser (caminho)
No Unix e no Windows, retorne o argumento com um componente inicial de ~ ou ~ user substituído pelo diretório inicial do usuário.

No Unix, um ~ inicial é substituído pela variável de ambiente HOME, se estiver configurada; caso contrário, o diretório inicial do usuário atual será consultado no diretório de senhas por meio do módulo interno pwd. Um ~ usuário inicial é procurado diretamente no diretório de senhas.

Jacob Vlijm
fonte
11
Em geral, você nunca deve assumir que a expansão til é feita no nível do sistema operacional, é algo que os shells unix (e nem todos eles!) Fazem por você.
Farsil 7/03
1
Eu acho que a questão mais relevante está alinhada na resposta de alexis: a posição de ~na lista de argumentos do shell.
David Foerster
@ Farsil, eu discordo. Os programas podem ser portáteis, mas quando você os executa na linha de comando, você o faz em um sistema específico. E não vamos esquecer que este é askubuntu.com, e Ubuntu é sempre Unix ( tanto quanto sabemos :-)
alexis
1
@alexis: O Ubuntu também não faz expansão de nível no sistema operacional. Ainda é uma funcionalidade shell.
user2357112
1
Acho que você está rachando os cabelos. Ninguém disse que o kernel está fazendo isso. O ponto é que isso não é feito pelo programa que aceita os argumentos.
31417 Alexis
12

A expansão do til é feita apenas em alguns contextos que variam ligeiramente entre as conchas .

Enquanto é realizado em:

var=~

Ou

export var=~

em algumas conchas. Não está em

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

em cartuchos POSIX.

É no bashentanto, quando não está no modo de conformidade POSIX (como quando chamado como shou quando POSIXLY_CORRECTestá no ambiente):

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

No entanto, é somente quando o que está à esquerda do =é modelado como um nome de variável válido não citado, portanto, embora ele fosse expandido cmd prefix=~, ele não estaria cmd --prefix=~(como --prefixnão é um nome de variável válido) nem em cmd "p"refix=~(por causa do citado p) nem no var=prefix; cmd $var=~.

Em zsh, você pode definir a magic_equal_substopção ~a ser expandida após qualquer aspas =.

$ 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

No caso de ~(ao contrário de ~user), você pode apenas usar $HOME:

cmd --whatever="$HOME/whatever"

~expande para o valor de $HOME. Se $HOMEnão estiver definido, o comportamento varia entre as conchas. Algumas conchas consultam o banco de dados do usuário. Se você quiser levar isso em conta, poderá fazer (e é também isso que você precisaria fazer ~user):

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

Em qualquer caso, em conchas que não sejam zshlembradas, é necessário citar expansões variáveis!

Stéphane Chazelas
fonte
1
O manual de referência de Bash parece dizer que os tildes são expandidos apenas em atribuições variáveis ​​e no início de uma palavra; portanto, expandi-lo echo a=~parece contradizer o manual.
precisa saber é o seguinte
@ilkkachu, sim, o manual está incompleto. Também não especifica claramente em que contexto ~será expandido (o que se entende por "palavra"). Veja o link na parte superior da resposta para mais detalhes.
Stéphane Chazelas
6

~possui regras de expansão específicas, que seu comando não atende. Especificamente, ele é expandido apenas quando não está entre aspas, no início de uma palavra (por exemplo python ~/script.py) ou no início de uma atribuição de variável (por exemplo PYTHONPATH=~/scripts python script.py). O que você tem é --data_path=~/blablaqual é uma única palavra em termos de shell, para que a expansão não seja realizada.

Uma correção imediata é usar a $HOMEvariável shell, que segue regras regulares de expansão de variáveis:

python ptb_word_lm.py --data_path=$HOME/blabla
Dmitry Grigoryev
fonte
Isso é um pouco simplificado demais, existem outros contextos em que a expansão til é executada como em PATH=$PATH:~/bin. Também isso $HOMEprecisa ser citado ou split + glob se aplica a conchas diferentes de zsh.
Stéphane Chazelas 7/03/2017
@sch desculpe, mas o link que você forneceu no comentário leva a uma pergunta sobre o mouse óptico, sem mencionar a expansão til. Você pode explicar isso?
Sergiy Kolodyazhnyy 8/0317
Boa resposta. Ele resume basicamente os bashestados do manual na Tilde Expansionseção. 1
Sergiy Kolodyazhnyy
Desculpe, eu estou tão acostumado a usar links intra-site no unix.SE, pois [link](/a/146697)não percebi que estávamos em um site diferente aqui. O link deve estar
Stéphane Chazelas 8/17/17