10

The below example explains the issue. Why is the FILENAME printed correctly when echoed and perceived as a pattern when using substitution?

#!/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
15
FILEPATH_WITH_GLOB="/home/user/file_*"

Now, FILEPATH_WITH_GLOB contains /home/user/file_*

FILENAME=$(basename "$FILEPATH_WITH_GLOB")

FILENAME contains file_*.

echo $FILENAME                #file_1234

$FILENAME being unquoted in list context, that expansion undergoes the split+glob operator, so that's expanded to the list of matching file: filename generation is performed upon parameter expansion.

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

It's still an unquoted parameter expansion in list context, so still undergoes split+glob. However here, the ile_* pattern doesn't match any file, so it expands to itself instead.

What you probably want here is:

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

Or you can store them in an array:

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

If you only care about the first match, or you know there's only one match, you can then refer to that file as $files. bash has that usually annoying behaviour that $files expands to ${files[0]} instead of all the elements of the array (a behaviour inherited from ksh, fixed in zsh), but here, that would be a wanted behaviour for once.

  • Thanks for the explanation. Managed to do a workaround with FILEPATH_WITH_GLOB=`echo /home/user/file_*` after your explanation. – TheMeaningfulEngineer Jun 17 '15 at 14:08
  • @Alan, that's the wrong way to address it. You want to use a array here. Either the positional parameters as in my example ($1, $2...) or a bash array like: files=(/home/user/file_*). – Stéphane Chazelas Jun 17 '15 at 14:18
  • (and all upper-case variables should really be reserved for environment variables, echo should not be used for arbitrary data, variables should not be left unquoted in list contexts). – Stéphane Chazelas Jun 17 '15 at 14:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.