первое
f="~$f" # в f изначально имена файлов формата типа /data/xxxx.xxx
лучше сразу привыкайте к относительным и абсолютным путям:
относительный/путь
и /абсолютный/путь
то есть, если вы в файл нагрепали относительных путей то не начинайте их с косой, здесь правильнее будет записывать как data/xxxx.xxx
а не /data/xxxx.xxx
. Также это позволит проще разбираться в коде где путь записывается в переменную, у вас это будет выглядеть как f=~/$f
второе
В скриптах,вместо тильды(~
), по возможности лучше всё же стараться использовать стандартную переменную $HOME
, как то f="$HOME/$f"
, что позволит не заморачиваться с запоминанием некоторых нюансов при раскрытие выражений.
третье
Раскрытие выражений (expansion)
Когда оболочка получает какую-то командную строку на выполнение, она
до начала выполнения команды осуществляет "грамматический разбор"
полученной командной строки. Одним из этапов такого "разбора" является
раскрытие или подстановка выражений (expansion). В bash имеется семь
типов подстановки выражений:
- раскрытие скобок (brace expansion);
- замена знака тильды (tilde expansion);
- подстановка параметров и переменных;
- подстановка команд;
- арифметические подстановки (выполняемые слева направо);
- разделение слов (word splitting);
- раскрытие шаблонов имен файлов и каталогов (pathname expansion).
Все эти операции выполняются именно в том порядке, как они здесь
перечислены. Рассмотрим их последовательно.
повторю, порядок раскрытия важен.
В упрощенном варианте ваша проблема выглядит так:
#!/bin/bash
set -o xtrace
touch ~/test.file
f="~/test.file"
[ -f "$f" ]; echo $?
# test -f "$f"; echo $?
rm -f ~/test.file
exit
отладочный вывод через xtrace
+ touch /home/.../test.file
+ f='~/test.file'
+ '[' -f '~/test.file' ']'
+ echo 1
1
+ rm -f /home/.../test.file
+ exit
где видно, что из за того что путь при присвоении переменной f
закавычен то замена знака тильды(~
) не произошла, а далее при раскрытии выражения в test
замена знака тильды также не происходит потому что её попусту еще не видно, а видно её тока после подстановки переменной f
, но как говорится - "поезд уже ушёл" (подстановка сначала тильды, а потом переменной, но не наоборот)
Вот и выходит что test
смотрит файл по относительному пути ~/test.file
где ~
это не домашняя директория пользователя, а реальное название папки в текущей директории и естественно не находит такой.
дополнение
Если я правильно понял обьяснение, как ОС работает с тильдой...
нет, ОС не работает с тильдой, в данном случае с тильдой работает командный интерпретатор BASH.
такая конструкция
file='/data/1.txt'
if [ -f "~$file" ]
должна работать, ведь здесь тильда явно задается сразу в test? А она
тоже выдает False... Для True у меня на компе нужно именно явно задать
и тильду, и имя файла...
вы должны были уже понять что замена тильды в кавычках неработает и всегда интерпритирутся bash как обычный символ ~
; правильно здесь будет тильду вывести за кавычки:
[ -f ~"$file" ]
но и это еще не всё, чтобы правильно раскрыть тильду в слове, должны соблюдаться ряд условий которые описанны в вышеупомянутой мной статье Раскрытие выражений (expansion) раздела 5.7.2 Замена тильды (Tilde Expansion), если кратко то примерно так:
echo "~" # ~
echo ~"/" # ~/
echo ~"xyz" # ~xyz
echo ~ # /home/$USER
echo ~/ # /home/$USER/
echo ~/"xyz" # /home/$USER/xyz
посему верным будет такая запись:
file='data/1.txt'
[ -f ~/"$file" ]
но с вашим упорством ставить слешь в начале относительного пути мугу предложить разве что обходной вариант:
file='/data/1.txt'
[ -f $(printf "%s%s" ~ "$file") ]