22

I want to find all files by length of the file names.

For example if I want to find files of length 1, such as a.go, b.go.

I put:

grep '.\{1\}' file

But this does not work. What command can I use to find files by file name length?

1
  • 1
    a.go is a filename of length 4. a is a filename of length 1. This is Unix, not DOS where the name and extension were separate and an implicit . was always part of the filename. Aug 16, 2015 at 14:52

10 Answers 10

21

If you just want to find the filenames, you can use the following command:

find -exec basename '{}' ';' | egrep '^.{100,}$'

That will run find, pulling off the name of the file or directory using basename and then look for any filename or directory name that is at least 100 characters. If you wanted to find a filename of an exact length, use {100} instead of {100,}.

If you want to find the filenames including the path, do this:

find | egrep '/[^/]{100,}$'

This will return the filename including the relative path to where you ran the find command.

6

grep looks for patterns in the contents of the files. If you want to look at the names of the files, you can just use a shell glob (if you want to consider only filenames in the current directory):

echo ?.go

(By the way, it's the shell that evaluates the glob, not the echo command.)

If you want to look in directories recursively, starting with the current directory, the tool to use is find:

find . -name '?.go'

(Note that you have to quote the pattern to prevent the shell from evaluating it as a glob before find gets invoked.)

2
  • 1
    This will also find directories that also match the pattern '?.go'.
    – slm
    Oct 1, 2013 at 4:36
  • @slm, directories are one of many types of files, the OP didn't specify what type of files he was after. Aug 15, 2015 at 21:05
5

First of all, grep searches through the contents of files, it will not search for file names. For that you want find. It does not have an option to set the filename length but you can parse it's output to get what you want. Here are some examples that I run in a directory containing the following files:

$ tree
.
├── a
├── ab
├── abc
├── abcd
└── dir
    ├── a
    ├── ab
    ├── abc
    └── abcd

Searching for files in this directory returns:

$ find . -type f | sort
./a
./ab
./abc
./abcd
./dir/a
./dir/ab
./dir/abc
./dir/abcd

So, to find the files with a length of X, we will need to remove the path and match only the characters after the last /:

 $ find . -type f | grep -P '/.{3}$'

The sed command just removes the ./. The -P flag activates Perl Compatible Regular Expressions which are needed for {n} which is a PCRE thing and the $ means "match the end of the string".

You could also simply do this:

find . -type f | grep -P '/...$'

That's fine for small numbers typing 15 dots to match filenames of length 15 is not very efficient.

Finally, if you want to ignore the extension and match only the file's name, do this (as @slm suggested):

find . -type f | grep -P '/.{1}\.'

However, this will also find files like a.bo.go. This is better if your filenames can contain more than one . :

find . -type f | grep -P '/.{1}\.[^.]+$'

NOTE: The solution above assumes that you have relatively sane filenames that don't contain new line characters etc. It will also count not ignore spaces in filenames when calculating the length which might not be what you want. If either of these is a problem, let me know and I'll update my answer.

2
  • {x,y} is also in standard extended regexps (as in grep -E), it's not perl (let alone PCRE) specific. Standard BREs also have \{x,y\} (added there before {x,y} was added in EREs) Sep 2, 2023 at 7:56
  • find . -type f | grep -P '/.{3}$' also matches on ./x/y Sep 2, 2023 at 7:56
2

If you have GNU find, you can just do:

find . -type f -regextype 'posix-basic' -iregex '.*/.\{1\}\.go$'
./a.go
./dir1/b.go
./dir2/subdir1/subdir2/a.go

find . -type f -regextype 'egrep' -iregex '.*/.{2}\.go$'
./ab.go
./dir1/zx.go
./dir2/subdir1/subdir2/xo.go

you can use different -regextype based on your need.

valid types are ‘findutils-default’, ‘awk’, ‘egrep’, ‘ed’, ‘emacs’, ‘gnu-awk’, ‘grep’, ‘posix-awk’, ‘posix-basic’, ‘posix-egrep’, ‘posix-extended’, ‘posix-minimal-basic’, ‘sed’.

1

You don't specify but if the files are within a directory structure you can use find to locate files that are length 1, with the help of grep:

$ find . -type f  | grep -P '/.{1}\.'

Example

$ find . -type f  | grep -P '/.{1}\.' | head -10
./util-linux-2.19/include/c.h
./88366/a.bash
./89186/a.bash
./89563/b.txt
./89563/a.txt
./89563/c.txt
./89563/d.txt
./91734/2.c
./91734/4.c
./91734/1.c
2
1

I would use something like this.

find . -type f -name "?.go"

So if you put two "?" then you are looking for file names with two characters and the extension.

0

Here is a working solution using ls although it's a bit silly. It basically creates a string consisting of question marks that correspond to the desired file name length and then allows for shell expansion of that as a wildcard, giving the resulting filenames to the ls command (and whatever switches you supply to it ahead of the filenames, of course - e.g., -l):

# Tested on CentOS 7

###############################
# With a fixed length (of 32) #
###############################
ls $(for i in {1..32};do echo -n '?';done)

##########################
# With a variable length #
##########################
# Set the filename length (or get it from elsewhere)
declare -x filename_len=32

# The following command can be employed
ls $(eval "for i in {1..$filename_len};do echo -n '?';done")

# Or, because eval is considered evil, the better alternative
ls $(seq -s ' ' 1 $filename_len | sed 's/[[:digit:]]\+ \?/?/g')

# Many other approaches can be used to do something similar
# (e.g., perl, etc...) to generate the question marks
ls $(perl -e 'print "?"x$ENV{filename_len}')
# If you're bothered by the quotes in the above around the question
# mark, since no interpolation is needed, you can change:
# "?" to '"'?'"'  - in this case, I'd leave the quotes for clarity.

unset filename_len

One caveat here is that the resulting list of filenames could be longer than the shell allows and that is something you'd have to consider. That's why a find/xargs approach is generally best if the number of files is unbounded or unknown.

0

When you want to use a glob in find command, you should use quotes, such as:

find /var/log -iname "*.err" 

for your case use:

find yourpath -type f -a -name "?.go"

You should replace yourpath with your real path.

  • -a means "and"
  • -type f means you are looking for regular files only.
  • ? in your query means one character such as DOS if you recall.
0

For completeness, to find regular files whose name has one and only one character excluding the extension, or in other words one character before the first dot if any:

find . '(' -name '[!.]' -o -name '[!.].*' ')' -type f

For three characters:

find . '(' -name '[!.][!.][!.]' -o -name '[!.][!.][!.].*' ')' -type f

You get the gist.

Note that with those, for a file called .v2.tar.gz the extension is v2.tar.gz and the rest is empty (of length 0).

With zsh, you can also do:

set -o extendedglob
print -rC1 -- **/[^.](#c3)(|.*)(ND.)

Where the (#cx[,y]) extendedglob is the equivalent of regexp {x[,y]} to match from x to y of the preceding thing (or exactly x if ,y is omitted) , **/ is any level of subdirectories, N for Nullglob, D for Dotglob, . the equivalent of find's -type f.

-3

ls ?.* should give you desired results.

2
  • 2
    welcome to Unix.SE. It's unfortunately not useful to add answers to old questions that already have lots of good answers. (Unless there's a new better way to solve an old problem, or you have a better solution than any of the old answers.) If you want to answer some questions, look at the new questions on the front page. Aug 16, 2015 at 14:50
  • ls: cannot access '?.*': No such file or directory
    – bfontaine
    Aug 31, 2023 at 9:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .