使用从 glob 模式定义的变量的 Bash 替换

The*_*eer 10 shell bash shell-script quoting wildcards

下面的例子解释了这个问题。为什么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
Run Code Online (Sandbox Code Playgroud)

Sté*_*las 15

FILEPATH_WITH_GLOB="/home/user/file_*"
Run Code Online (Sandbox Code Playgroud)

现在,FILEPATH_WITH_GLOB包含/home/user/file_*

FILENAME=$(basename "$FILEPATH_WITH_GLOB")
Run Code Online (Sandbox Code Playgroud)

FILENAME包含file_*.

echo $FILENAME                #file_1234
Run Code Online (Sandbox Code Playgroud)

$FILENAME在列表上下文中不加引号,该扩展经历 split+glob 运算符,因此扩展到匹配文件的列表:文件名生成参数扩展时执行。

echo ${FILENAME:1:5}          #ile_*   <---why is this not ile_1
Run Code Online (Sandbox Code Playgroud)

在列表上下文中它仍然是一个不带引号的参数扩展,所以仍然会经历 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
Run Code Online (Sandbox Code Playgroud)

或者您可以将它们存储在数组中:

shopt -s nullglob
files=(/home/user/file_*)
Run Code Online (Sandbox Code Playgroud)

如果您只关心第一个匹配项,或者您知道只有一个匹配项,则可以将该文件称为$files. bash具有通常令人讨厌的行为,而不是$files扩展到${files[0]}数组的所有元素(从 继承的行为ksh,固定在 中zsh),但在这里,这将是一次想要的行为。