Чому я не міг використати "~" замість "/ 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?

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




@OskarSkog Чи не повинен оболонка розширюватись до ~того, як аргумент буде передано python? Так само, як оболонка розширить зворотні косою стрілки на шляху або видалить лапки, якщо шлях був цитується.
Мішель Джонсон

1
На відміну від $VARIABLES, ~розширюється лише на початку рядка.
alexis

@OskarSkog, «Python не знає , що означає ~» означає , що проблема є специфічною для Python не вистачає шматка функціональності, створення необґрунтованого очікування , що така функціональність (виконань розширення після того exec«г) повинна бути широко доступна в інструментах UNIX .
Чарльз Даффі

Відповіді:


45

Вам потрібно зрозуміти, що ~зазвичай розширюється оболонкою; програми, яких ви називаєте, ніколи не бачать, вони бачать повне ім'я шляху, вставлене bash. Але це відбувається лише тоді, коли тильда знаходиться на початку аргументу (і не цитується).

Якщо програма Python, яку ви запускаєте, використовує модуль, подібний getoptдо аналізу свого командного рядка, ви можете навести аргумент --data-pathопції як окреме "слово", щоб дозволити розширення tilde:

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

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

PS. Тильда також розширюється на початку виразу присвоєння змінної оболонки типу DIRNAME=~/anaconda2; хоча тильда у вашому запитанні також відповідає знаку рівності, це використання не має особливого значення для оболонки (це просто щось передане програмі) і не викликає розширення.


6
Якщо ви вже не знаєте getopt , використовуйте, argparseякщо ви пишете Python.
Нік Т

Я додав argparseвідповідь, оскільки це головна альтернатива, але особисто мені здається, що це набагато складніше, ніж getopt, не простіше. YMMV.
alexis

33

Розширення тильди в пітоні

Відповідь коротка та проста:

python не розширюється, ~якщо ви не використовуєте:

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

Дивіться також тут :

os.path.expanduser (шлях)
У Unix та Windows повертайте аргумент із початковим компонентом користувача ~ або ~, заміненим на домашній каталог цього користувача.

У Unix початковий ~ замінюється змінною середовища HOME, якщо він встановлений; в іншому випадку домашній каталог поточного користувача шукається в каталозі паролів через вбудований модуль pwd. Початкового ~ користувача шукають безпосередньо в каталозі паролів.


11
Загалом, ніколи не слід вважати, що розширення tilde відбувається на рівні ОС, це те, що оболонки Unix (і не всі вони!) Роблять для вас.
farsil

1
Я думаю, що більш відповідне питання викладено у відповіді alexis: позиція ~у списку аргументів оболонки.
Девід Фоерстер

@farsil, я не згоден. Програми можна зробити портативними, але коли ви запускаєте їх з командного рядка, ви робите це в певній системі. І не будемо забувати, що це askubuntu.com, а Ubuntu - це завжди Unix ( наскільки ми знаємо :-)
alexis

1
@alexis: Ubuntu також не виконує розширення на тилі на рівні ОС. Це все ще функціональність оболонки.
user2357112

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
Довідковий посібник Баша, схоже, говорить про те, що тильди розгортаються лише на змінні призначення та на початку слова, тому розширення на нього, 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потрібно котирувати або розділяти + глобул, застосовується і в оболонках, окрім zsh.
Стефан Шазелас

@sch вибачте, але посилання, яке ви вказали в коментарі, приводить до питання про оптичну мишу, без згадки про розширення тильди. Чи можете ви поясніть це?
Сергій Колодяжний

Хороша відповідь. Він в основному підсумовує те, що bashпосібник містить Tilde Expansionрозділ. +1
Сергій Колодяжний

Вибачте, я настільки звик використовувати внутрішньосайтові посилання в unix.SE, оскільки [link](/a/146697)не зрозумів, що ми тут на іншому сайті. Посилання має бути там
Стефан Шазелас
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.