Shells like Bash and Zsh expand wildcard into arguments, as many arguments as match the pattern:

$ echo *.txt
1.txt 2.txt 3.txt

But what if I only want the first match to be returned, not all the matches?

$ echo *.txt

I don't mind shell-specific solutions, but I would like a solution that works with whitespace in filenames.

  • ls *.txt | head -1 ? – Archemar Sep 18 '14 at 9:41
  • 1
    @Archemar: doesn't work with newlines in filenames. – Flimm Sep 18 '14 at 9:44

One robust way in bash is to expand into an array, and output the first element only:

files=( $pattern )
echo "${files[0]}"  # printf is safer!

(You can even just echo $files, a missing index is treated as [0].)

This safely handles space/tab/newline and other metacharacters when expanding the filenames. Note that locale settings in effect can alter what "first" is.

You can also do this interactively with a bash completion function:

_echo() {
    local cur=${COMP_WORDS[COMP_CWORD]}   # string to expand

    if compgen -G "$cur*" > /dev/null; then
        local files=( ${cur:+$cur*} )   # don't expand empty input as *
        [ ${#files} -ge 1 ] && COMPREPLY=( "${files[0]}" )
complete -o bashdefault -F _echo echo

This binds the _echo function to complete arguments to the echo command (overriding normal completion). An extra "*" is appended in the code above, you can just hit tab on a partial filename and hopefully the right thing will happen.

The code is slightly convoluted, rather than set or assume nullglob (shopt -s nullglob) we check compgen -G can expand the glob to some matches, then we expand safely into an array, and finally set COMPREPLY so that quoting is robust.

You can partly do this (programmatically expand a glob) with bash's compgen -G, but it's not robust as it outputs unquoted to stdout.

As usual, completion is rather fraught, this breaks completion of other things, including environment variables (see the _bash_def_completion() function here for the details of emulating the default behaviour).

You could also just use compgen outside of a completion function:

files=( $(compgen -W "$pattern") )

One point to note is that "~" is not a glob, it's handled by bash in a separate stage of expansion, as are $variables and other expansions. compgen -G just does filename globbing, but compgen -W gives you all of bash's default expansion, though possibly too many expansions (including `` and $()). Unlike -G, the -W is safely quoted (I can't explain the disparity). Since the purpose of -W is that it expands tokens, this means it will expand "a" to "a" even if no such file exists, so it's perhaps not ideal.

This is easier to understand, but may have unwanted side-effects:

_echo() {
    local cur=${COMP_WORDS[COMP_CWORD]}
    local files=( $(compgen -W "$cur") ) 
    printf -v COMPREPLY %q "${files[0]}"  


touch $'curious \n filename'

echo curious*tab

Note the use of printf %q to safely quote the values.

One final option is to use 0-delimited output with GNU utilities (see the bash FAQ):

while IFS= read -r -d $'\0' filename; do 
    printf '%q' "$filename"; 
done < <(find . -maxdepth 1 -name "$pattern" -printf "%f\0" | sort -z )

This option gives you a little more control over the sorting order (the order when expanding a glob will be subject to your locale/LC_COLLATE and may or may not fold case), but is otherwise a rather large hammer for such a small problem ;-)

| improve this answer | |

In zsh, use the [1] glob qualifier. Note that even though this special case returns at most one match, it's still a list, and globs are not expanded in contexts that expect a single word such as assignments (array assignments aside).

echo *.txt([1])

In ksh or bash, you can stuff the whole list of matches in an array and use the first element.

echo "${tmp[0]}"

In any shell, you can set the positional parameters and use the first one.

set -- *.txt
echo "$1"

This clobbers the positional parameters. If you don't want that, you can use a subshell.

echo "$(set -- *.txt; echo "$1")"

You can also use a function, which has its own set of positional parameters.

set_to_first () {
  eval "$1=\"\$2\""
set_to_first f *.txt
echo "$f"
| improve this answer | |
  • 1
    And to get the first $n$ matches, you can use *.txt([1,n]) – Emre Mar 2 '16 at 21:39


for i in *.txt; do printf '%s\n' "$i"; break; done

A note that filename expansion is sorted according to the collating sequence in effect in the current locale.

| improve this answer | |

A simple solution:

sh -c 'echo "$1"' sh *.txt

Or use printf if you prefer.

| improve this answer | |

I just stumbled across this old question while wondering the same thing. I ended up with this:

echo $(ls *.txt | head -n1)

You can, of course, replace head with tail and -n1 with any other number.

The above wont work if you're working with files that have newlines in their name. To work with newlines you can use any of these:

  • ls -b *.txt | head -n1 | sed -E 's/\\n/\n/g' (Doesn't work on BSD)
  • ls -b *.txt | head -n1 | sed -e 's/\\n/\'$'\n/g'
  • ls -b *.txt | head -n1 | perl -pe 's/\\n/\n/g'
  • echo -e "$(ls -b *.txt | head -n1)" (Works with any special character)
| improve this answer | |
  • 3
    No, that will fail if the filename has newlines. – Isaac Aug 30 '18 at 1:48
  • 10
    what kind of crazy world do we live in where filenames contain newlines? – billynoah Nov 21 '18 at 18:10
  • @billynoah I personally think that Linux filesystems should ban newlines in filenames and require that filenames be valid UTF-8, but apparently, the decision-makers in filesystems disagree with me. – Flimm Jul 23 at 10:33

A use case I often encounter is defining either the top/bottom directory after glob expansion (e.g. a directory full of versioned SDKs or build tools). In this situation I'll usually want to save that directory name to a variable for use in a few places within a shell script.

This command usually does it for me:

export SDK_DIR=$(dirname /path/to/versioned/sdks/*/. | tail -n1)

Disclaimer: Glob expansion isn't going to sort your folders by semver; you've been warned. This is great if you have a Dockerfile that only one version of a directory, but that directories version could vary from image to image 🤷

| improve this answer | |
  • Welcome to U&L! That does handle most directory names, but it doesn't handle directory names with a newline. Try creating a directory like this mkdir "$(echo one; echo two)" and see what I mean. – Flimm Jun 6 '19 at 19:17
  • What is the advantage compared to the other alternatives, especially the version that uses tail? – RalfFriedl Jun 6 '19 at 19:57
  • Standard dirname only takes a single pathname, so you can't rely on it working on multiple pathnames unless you know your particular implementation supports it. – Kusalananda Jun 6 '19 at 20:55
  • @Flimm Good point; I think most developers would have bigger problems to deal with if their folder structure has newlines in it... I've never had to deal with that, and don't expect to with any half-decent containers and software I'm using – Andrew Odri Jun 6 '19 at 20:57
  • @RalfFriedl Good question; this essentially filters out anything that isn't a valid directory (and won't list/traverse . and ..) – Andrew Odri Jun 6 '19 at 20:58

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.