Присвоение результата "теста" переменной

Я пишу script, где мне нужно использовать вывод теста файла в нескольких местах, в том числе внутри оболочки. Я хотел бы присвоить существование файла переменной оболочки, например: file_exists=[ -f $myfile ].

Просто чтобы убедиться, что у меня есть мои базы, я начинаю с касания файла и тестирования его существования:

file='a'
touch $file
if [ -f $file ]
then
    echo "1 -- '$file' exists"
fi

Вывод:

1 -- 'a' exists

Файл был создан успешно - никаких сюрпризов, но, по крайней мере, я знаю, что я не имею никаких проблем с разрешениями или что-то еще.

Далее я проверяю, чтобы я мог хранить логическое выражение в переменной:

mytest=/bin/true

if $mytest
then
    echo "2 -- \$mytest is true"
fi

Вывод:

2 -- $mytest is true

Итак, у меня есть основные понятия - условные выражения должны выдавать тот же результат, что и /bin/true или /bin/false... но это не то, что я вижу:

mytest=[ -f $file ]
if $mytest
then
    echo "3 -- \$mytest is true [expect true]"
else
    echo "3 -- \$mytest is false [expect true]"
fi

Это не выполняется со следующей ошибкой:

-f: command not found

Я получаю то же сообщение об ошибке, если я использую test -f $file, а не [ -f $file ].

Если я поместил пробел перед [, ошибка исчезнет...

mytest= [ -f $file ]
if $mytest
then
    echo "4 -- \$mytest is true [expect true]"
else
    echo "4 -- \$mytest is false [expect true]"
fi

Выход кажется правильным:

4 -- $mytest is true [expect true]

... но если я удалю файл, я должен получить противоположный результат:

rm $file
mytest= [ -f $file ]
if $mytest
then
    echo "5 -- \$mytest is true [expect false]"
else
    echo "5 -- \$mytest is false [expect false]"
fi

... и я не делаю:

5 -- $mytest is true [expect false]

Чтобы быть справедливым, я ожидал, что пространство испортится с истинным значением:

mytest= /bin/false
if $mytest
then
    echo "6 -- \$mytest is true [expect false]"
else
    echo "6 -- \$mytest is false [expect false]"
fi

Выходы:

6 -- $mytest is true [expect false]

Итак, как я могу сохранить вывод из test встроенного в переменную оболочки?

Ответ 1

Как описано в других документах, использование строки "true" - это красная селедка; это не является подходящим способом хранения логических значений в сценариях оболочки, поскольку его оценка означает динамическое выключение команды, а не просто проверку сохраненного значения с помощью встроенных оболочек оболочки, жестко закодированных в вашем script.

Вместо этого, если вы действительно должны сохранить статус выхода, сделайте это как числовое значение:

[ -f "$file" ]               # run the test
result=$?                    # store the result

if (( result == 0 )); then   # 0 is success
  echo "success"
else                         # nonzero is failure
  echo "failure"
fi

Ответ 2

Вам нужно процитировать пробелы:

mytest='[ -f $file ]'
if $mytest; then echo yes; fi

Однако это чрезвычайно хрупкое и потенциально небезопасное. См. http://mywiki.wooledge.org/BashFAQ/050 для подробного обсуждения и некоторых лучших способов добиться чего-то подобного.

Если вы хотите инкапсулировать сложный фрагмент кода, обычно это способ:

mytest () { [ -f "$file" ]; }
if mytest; then echo yes; fi

Если вы хотите запустить код один раз и сохранить его результат, чтобы вы могли его изучить позже, я бы перефразировал его следующим образом:

if [ -f "$file" ]; then
    mytest=true
else
    mytest=false
fi
if $mytest; then echo yes; fi

Ответ 3

mytest=/bin/true хранит строку /bin/true в переменной $mytest.

mytest=[ -f $file ] устанавливает переменную $mytest в значение [ для продолжительности команды -f $file ] (которая по мере того, как ваш вывод указывает на ошибку, поскольку нет команды -f).

mytest= [ -f $file ] (как и выше) устанавливает значение переменной $mytest пустым в течение команды [ -f $file ] (и возвращает все [).

mytest= /bin/false это то же самое, что и в приведенном выше случае, только команда запускается /bin/false.

Если вы хотите сохранить код возврата из команды в переменной, вы можете сделать

/bin/true
ret=$?

если вы хотите сохранить вывод из команды в переменной, которую вы можете сделать

out=$(/bin/true)

(хотя при /bin/true эта переменная будет пуста, так как она не выводит текст.

Для вашего случая вам нужна прежняя модель $?.

Кроме того, использование set -x (и/или set -v) в ваших скриптах могло помочь вам диагностировать это.

Ответ 4

Старый, но оставил это здесь для справки людям, которым это может понадобиться. Не самое красивое решение, но оно работает в bash:

mytest=$( [ -f $file ] ; echo $? )

Более переносимый, используя команду test и обратные ссылки:

set mytest='test -f $file ; echo $?'

В подпроцессе (<!> Загрузка системы) выполняется оценка условия, а затем результат отражается в выводе, который захватывается переменной $ mytest.