Подстановка Bash с переменной, определенной из шаблона glob

10

Приведенный ниже пример объясняет проблему. Почему FILENAMEпри использовании подстановки отпечаток корректно отображается и воспринимается как шаблон?

#!/bin/bash

FILEPATH_WITH_GLOB="/home/user/file_*"
FILENAME=$(basename "$FILEPATH_WITH_GLOB")
echo $FILENAME                #file_1234
echo ${FILENAME:1:5}          #ile_*   <---why is this not ile_1
TheMeaningfulEngineer
источник

Ответы:

15
FILEPATH_WITH_GLOB="/home/user/file_*"

Теперь FILEPATH_WITH_GLOBсодержит/home/user/file_*

FILENAME=$(basename "$FILEPATH_WITH_GLOB")

FILENAMEсодержит file_*.

echo $FILENAME                #file_1234

$FILENAMEв кавычках в контексте списка это расширение подвергается оператору split + glob, поэтому он расширяется до списка соответствующего файла: генерация имени файла выполняется после раскрытия параметра .

echo ${FILENAME:1:5}          #ile_*   <---why is this not ile_1

Это все еще расширение параметров без кавычек в контексте списка, поэтому все еще подвергается split + glob. Однако здесь ile_*шаблон не соответствует ни одному файлу, поэтому он расширяется до самого себя.

Что вы, вероятно, хотите здесь:

shopt -s nullglob # have globs expand to nothing when they don't match
set -- /home/user/file_* # expand that pattern into the list of matching 
                         # files in $1, $2...
for file do  # loop over them
  filename=$(basename -- "$file")
  printf '%s\n' "$filename" "${filename:1:5}"
done

Или вы можете хранить их в массиве:

shopt -s nullglob
files=(/home/user/file_*)

Если вы заботитесь только о первом совпадении или знаете, что совпадение только одно, вы можете обратиться к этому файлу как $files. bashимеет это обычно раздражающее поведение, которое $filesрасширяется ${files[0]}вместо всех элементов массива (поведение, унаследованное от ksh, зафиксированное в zsh), но здесь, это будет желаемое поведение на этот раз.

Стефан Шазелас
источник
Спасибо за объяснение. Удалось сделать обходной путь FILEPATH_WITH_GLOB=`echo /home/user/file_*` после вашего объяснения.
TheMeaningfulEngineer
@ Алан, это неправильный способ решить эту проблему. Вы хотите использовать массив здесь. Либо позиционные параметры , как в моем примере ($ 1, $ 2 ...) или bashмассив , как: files=(/home/user/file_*).
Стефан Шазелас
(и все переменные в верхнем регистре действительно должны быть зарезервированы для переменных среды, echoне должны использоваться для произвольных данных , переменные не должны оставаться без кавычек в контексте списка).
Стефан Шазелас