Не работает правильно команда test с переменной

Рейтинг: -2Ответов: 1Опубликовано: 01.04.2023

Помогите разобраться с лабой: команда test, когда он задан через переменную. При чем я вывел значение переменной в файл (1.txt, ниже в коде видно), скопировал имя файла из файла и скормил test'у непосредственно строкой (чтоб исключить возможную проблему с кодировкой символов в имени файла), и все работает. Я уже не знаю, что подозревать...

#!/bin/bash

for f in $files
do
  f="~$f"       # в f изначально имена файлов формата типа /data/xxxx.xxx
  echo "$f">>1.txt #просто контроль, что же я буду передавать команде test...
  if [ -f "$f" ] ; then  # проблема здесь - результат False (((
    echo "$f" >> ~/data/oldFiles.txt
  fi
done

Дополнение после ответа форумчанина:

Спасибо за ответ! Если я правильно понял обьяснение, как ОС работает с тильдой, то тогда такая конструкция

file='/data/1.txt'
if [ -f "~$file" ]

должна работать, ведь здесь тильда явно задается сразу в test? А она тоже выдает False... Для True у меня на компе нужно именно явно задать и тильду, и имя файла...

Ответы

▲ -1Принят

первое

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") ]