6

How do you get the first file in a directory in bash? First being the what the shell glob finds first.

My search for getting the first file in a directory in bash brought me to an old post with a very specific request. I'd like to document my solution to the general question for posterity and make a place for people to put alternative solutions they'd like to share.

2
  • By "file", do you mean "regular file", or are you also interested in directories, symbolic links and other types of files?
    – Kusalananda
    Sep 25 '20 at 6:26
  • Good point. I was only considering regular files. Nov 2 '20 at 1:52
13

To get the first file in the current dir you can put the expansion in an array and grab the first element:

files=(*)
echo "${files[0]}"
# OR
echo "$files" # since we are only concerned with the first element

Assuming your current dir contains multiple dirs you can loop through and grab the first file like so:

for dir in *; do
    files=($dir/*)    
    echo "${files[0]}"
done
1
2

Almost is an this answer:

shopt -s nullglob
set -- *
printf "%s\n" "$1"
1

In zsh:

  • first non-hidden file in locale collation order: first=(*(N[1]))
  • same, but restricted to non-directory files: first=(*(N^/[1]))
  • same, but also excluding symlinks to directories: first=(*(N^-/[1]))
  • restricting to regular files: first=(*(N.[1]))
  • same but including symlinks to regular files: first=(*(N-.[1]))

Some more notes:

  • those define an array variable as it still needs to be able to store a variable number of elements: 0 (no matching file) or 1 (matching files, among which only the first is selected). To define a $first scalar variable instead (and have it contain the empty string if there's no matching file), you can do (){ first=$1; } *(N[1]) instead. Or to leave the $first scalar variable untouched instead if there's no matching file: (){ (($#)) && first=$1; } *(N[1]).
  • to include hidden files, add the D glob qualifier
  • in some locales (including most of the ones typically used on GNU systems from 2020 like en_US.UTF-8), collation order is not always deterministic as some characters sort the same. See for instance after touch 🧙 🧚 🧛 🧜 🧝 on Ubuntu 20.04. All those files will have the same sorting order, so which one you'll get first will me more or less random.
  • with zsh glob qualifiers, it's also possible to change the order: n makes the filename comparison numerical (so that file2 comes before file10 for instance), and with the o glob qualifier, one can sort based on other criteria than name (such as age, size...).
0

To get the name of the regular file that sorts first in a directory, you may use

shopt -s nullglob dotglob
unset name

for name in some/path/*; do
    [[ -f $name ]] && break
    unset name
done

After this loop, $name would either be the name of the regular file that sorts first, or it would be an empty string if there are no regular files in the diretory some/path.

The shell options used here makes sure that the loop is not run at all if the pattern does not match (nullglob), and that we also match hidden names (dotglob).

You'll get the first name since filename globbing patterns are expanded into lexicographically sorted lists.

-1

You can just use head:

ls -1 | head -n1
foo.txt

Note: read Why not parse ls (and what to do instead)?

1
-1

You may use printf to print the first argument only.

The external printf that recognize \c.

/bin/printf '%s\n\c' dir/*

To list the first sorted (file/directory) on the dir directory. If what is wanted is the pwd, then use ./*.

It may be useful to also set the bash options:

shopt -s nullglob dotglob

To avoid printing the glob when no file match and to avoid the dot-files.

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.