Почему я не могу использовать '~' вместо '/ home / username /' при указании пути к файлу

43

Я могу использовать ~вместо /home/username/указания путь к файлу, например, при разархивировании .zipфайла.

Однако сегодня, когда я следовал тем же путем, чтобы запустить пример RNN в терминале, tensorflow.python.framework.errors_impl.NotFoundErrorбыл брошен.

$ 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

Затем я заменил ~на /home/username/, и все заработало нормально.

Почему я не могу использовать ~вместо /home/username/указания пути к файлу при запуске примера RNN?

Не могли бы вы рассказать мне подробнее?

JNing
источник
@OskarSkog Разве оболочка не должна расширять ~перед передачей аргумента в python? Точно так же, как оболочка расширила бы обратный слеш в пути или удалила кавычки, если путь был заключен в кавычки.
Майкл Джонсон
1
В отличие от $VARIABLES, ~раскрывается только в начале строки.
Alexis
@OskarSkog, «Python не знает , что означает ~» означает , что проблема является специфичной для Python не хватает куска функциональности, создания необоснованного ожидания , что такая функциональность (выполнений расширения после того exec«г) должна быть широко доступна в инструментах UNIX ,
Чарльз Даффи

Ответы:

45

Вы должны понимать, что ~обычно расширяется оболочкой; программы, которые вы вызываете, никогда не видят его, они видят полный путь, вставленный bash. Но это происходит только тогда, когда тильда находится в начале аргумента (и не заключена в кавычки).

Если программа Python, которую вы запускаете, использует модуль, подобный getoptсинтаксическому анализу своей командной строки, вы можете задать аргумент --data-pathопции в виде отдельного слова, чтобы разрешить расширение тильды:

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

В вашем собственном коде вы можете использовать getoptили argparseдля обработки аргументов, а также можете вручную развернуть тильды, как подсказывает ответ @ JacobVlijm.

PS. Тильда также раскрывается в начале выражения присваивания переменной оболочки, подобного DIRNAME=~/anaconda2; хотя тильда в вашем вопросе также следует за знаком равенства, это использование не имеет особого значения для оболочки (это просто что-то, переданное программе) и не вызывает расширения.

Alexis
источник
6
Если вы уже не знаете getopt , используйте, argparseесли вы пишете Python.
Ник Т
Я добавил argparseк ответу, так как это основная альтернатива, но лично я нахожу его гораздо сложнее, чем getopt, а не проще. YMMV.
Alexis
33

Расширение тильды в питоне

Ответ короткий и простой:

Python не расширяется, ~если вы не используете:

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

Смотрите также здесь :

os.path.expanduser (путь)
В Unix и Windows верните аргумент с начальным компонентом ~ или ~ user, замененным домашним каталогом этого пользователя.

В Unix начальная переменная ~ заменяется переменной окружения HOME, если она установлена; в противном случае домашний каталог текущего пользователя ищется в каталоге паролей через встроенный модуль pwd. Начальный пользователь просматривается непосредственно в каталоге паролей.

Якоб Влейм
источник
11
В общем, вы никогда не должны предполагать, что расширение тильды выполняется на уровне операционной системы, это то, что оболочки Unix (и не все из них!) Делают для вас.
Farsil
1
Я думаю, что более актуальная проблема изложена в ответе Алексис: позиция ~в списке аргументов оболочки.
Дэвид Фёрстер
@farsil, я не согласен. Программы можно сделать переносимыми, но когда вы запускаете их из командной строки, вы делаете это в определенной системе. И давайте не будем забывать, что это askubuntu.com, а Ubuntu всегда Unix ( насколько мы знаем :-)
alexis
1
@alexis: Ubuntu также не выполняет расширение тильды на уровне ОС. Это все еще функциональность оболочки.
user2357112 поддерживает Monica
1
Мне кажется, что вы раскалываете волосы. Никто не сказал, что ядро ​​делает это. Дело в том, что это не сделано программой, которая принимает аргументы.
Alexis
12

Расширение тильды выполняется только в нескольких контекстах, которые слегка различаются между оболочками .

Пока это выполняется в:

var=~

Или

export var=~

в некоторых снарядах. Это не в

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

в оболочках POSIX.

bashХотя он находится не в режиме соответствия POSIX (например sh, когда POSIXLY_CORRECTвызывается как или когда находится в среде):

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

Однако это происходит только тогда, когда то, что находится слева от =формы, имеет форму имени действительной переменной без кавычек, поэтому, хотя оно будет расширено cmd prefix=~, оно не будет ни в cmd --prefix=~(как --prefixне допустимое имя переменной), ни в cmd "p"refix=~(из-за этого в кавычках p), ни в в var=prefix; cmd $var=~.

В zsh, вы можете установить magic_equal_substпараметр для ~расширения после любого без кавычек =.

$ 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

В случае ~(в отличие от ~user) вы можете просто использовать $HOMEвместо:

cmd --whatever="$HOME/whatever"

~расширяется до значения $HOME. Если $HOMEне установлено, поведение меняется между оболочками. Некоторые оболочки запрашивают базу данных пользователей. Если вы хотите принять это во внимание, вы можете сделать (и это также то, что вы должны были бы сделать ~user):

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

В любом случае, в других оболочках, кроме zshзапоминания, нужно указывать расширения переменных!

Стефан Шазелас
источник
1
Справочное руководство Bash, похоже, говорит, что тильды раскрываются только при назначении переменных и в начале слова, поэтому его расширение echo a=~противоречит руководству.
ilkkachu
@ilkkachu, да, руководство неполное. В нем также не указано, в каком контексте ~будет расширен (что означает «слово»). Смотрите ссылку в верхней части ответа для более подробной информации.
Стефан
6

~имеет определенные правила расширения, которые не удовлетворяет ваша команда. В частности, оно раскрывается только без кавычек, либо в начале слова (например python ~/script.py), либо в начале присвоения переменной (например PYTHONPATH=~/scripts python script.py). То, что у вас есть, --data_path=~/blablaэто одно слово в терминах оболочки, поэтому расширение не выполняется.

Немедленное решение - использовать $HOMEпеременную оболочки, которая следует обычным правилам расширения переменных:

python ptb_word_lm.py --data_path=$HOME/blabla
Дмитрий Григорьев
источник
Это немного упрощено, есть другие контексты, в которых расширение тильды выполняется как в PATH=$PATH:~/bin. Кроме того, это $HOMEдолжно быть заключено в кавычки или split + glob применяется в других оболочках, кроме zsh.
Стефан
@ Извините, но ссылка, которую вы указали в комментарии, приводит к вопросу об оптической мыши, без упоминания о расширении тильды. Можете ли вы объяснить это?
Сергей Колодяжный,
Хороший ответ. В нем кратко изложены основные положения bashруководства в Tilde Expansionразделе. +1
Сергей Колодяжный,
Извините, я настолько привык к использованию внутрисайтовых ссылок в unix.SE, [link](/a/146697)что даже не осознавал, что мы здесь на другом сайте. Ссылка должна быть там
Стефан