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
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
TheMeaningfulEngineer
  • 5,735
  • 15
  • 64
  • 113

1 Answers1

16
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.

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
  • 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](http://unix.stackexchange.com/q/65803), variables should not be left unquoted in list contexts). – Stéphane Chazelas Jun 17 '15 at 14:19