This question already has an answer here:

I would like to find files whose name has only 4 characters.

Example, there are three files under /tmp:

$ ls /tmp
txt
file
linux

Output should only show file because it only has 4 characters.

marked as duplicate by Michael Mrozek Aug 9 '15 at 20:38

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 1
    If you want to print also hidden files make use of brace expansion: echo /tmp/{.,?}???. – jimmij Jun 4 '15 at 12:38
  • What? No grep based solution yet? – Cole Johnson Jun 4 '15 at 14:56
  • 1
    @ColeJohnson It's not the best tool for the job. grep matches lines of text, but not all filenames are guaranteed to be single lines of text. – jw013 Jun 4 '15 at 17:46
  • 2
    Why do you want to do this? Please edit your question and enlighten us. – unforgettableid Jun 5 '15 at 20:06
  • 1
    Also, bytes or characters? beta in UTF-8 is 4 characters and 4 bytes long, while bétá is 4 characters and 6 bytes.... – Rmano Jun 6 '15 at 17:57
up vote 50 down vote accepted

Use the ? wildcard for file globbing:

ls -d /tmp/????

This will print all files and directories whose filename is 4-char long.

As suggested by @roaima, the -d flag will prevent ls to display the content of subdirectories that match the pattern.

  • 10
    +1 for not using find – frostschutz Jun 4 '15 at 11:24
  • 2
    Please open a separate question for this. – dr01 Jun 4 '15 at 12:16
  • 3
    Note that this won't list hidden file. – cuonglm Jun 4 '15 at 18:00
  • 4
    Use ls -d to avoid expansion of any directory matching /tmp/???? – roaima Jun 4 '15 at 21:06
  • 2
    @user913 if simple globbing does the same job, find is overkill. nothing wrong with it otherwise – frostschutz Jun 8 '15 at 20:46

List files in /tmp only:

cd /tmp
find . ! -name . -prune -path './????' -type f

List files in /tmp recursively:

find /tmp -path '*/????' -type f

Try:

find /tmp -type f -print| awk -F/ ' length($NF)  == 4 '

What awk does:

  • Using / as field separator,
  • Finding filename $NF (last field)
  • Computing length
  • And check if value is 4, then print it.
  • 2
    This won't work if filename contains newline. – cuonglm Jun 4 '15 at 13:31
  • @cuonglm can file name have new line? – Ooker Jun 5 '15 at 10:57
  • 1
    @Ooker: Yes, try touch a$'\n'b – cuonglm Jun 5 '15 at 11:17

This seems to me like the most straightforward way to find a file of four bytes:

find /tmp -type f -size 4c

Edit: to find a file name of four bytes:

find /tmp -type f -name '????'
  • 1
    Judging by the example given in the question, OP is looking at file names not contents. – jw013 Jun 4 '15 at 17:51
  • Ah, of course, not a file of four bytes but a file name of four bytes. That makes sense. – Ulric Eriksson Jun 4 '15 at 17:55
  • This will also match /tmp/somelongpathname/fred, which doesn't match the glob /tmp/???? – roaima Jun 4 '15 at 21:07
  • It is supposed to. /tmp/???? only finds files immediately under /tmp. – Ulric Eriksson Jun 4 '15 at 21:13
  • Can use -maxdepth arg to restrict to immediate entries of /tmp. The original question is unclear on the requirement. – Toby Speight Jun 5 '15 at 15:03

There's also a perl (5.10 or newer) solution:

perl -E 'say for </tmp/????>;'

A slightly more flexible version where you can specify the desired length:

perl -E 'my $w = "?" x shift; say for </tmp/$w>;' 4

Assuming you're using bash, you can do the following:

shopt -s globstar
shopt -s nullglob
filearray=(/tmp/**/????)

This will put the list of files you want in an array filearray. Newlines (and other exotic characters) in the filename will be handled correctly.

Setting globstar enables ** in glob patterns to match across subdirectories, giving the required recursive search.

Setting nullglob simply causes the expansion to expand to nothing if there are no matches. Otherwise it will expand to itself which is probably not what you want.

"/tmp/" takes 5 characters. That's why there is "9" (5+4) in test

for i in /tmp/* ;
  do [ "${#i}" -eq 9 ] && printf %s\\n "$i";
done

or

for i in /tmp/* ;
  do i="${i#/tmp/}"; # to get rid of /tmp/
  [[ "${#i}" -eq 4 ]] && printf %s\\n "$i"; # there is 4 in test!
done

FWIW. It does not fail on newline (@cuonglm), can be easilly converted to list only 140 character long file names (@don_crissti).

As globbing is done by the shell, there is even no need to call ls or find, echo does the trick:

$ echo /tmp/????
/tmp/file
$

Only thing is, if there is no file whose name is 4 characters long, it will just print

/tmp/????

You might want a one-liner bash script that checks this and also prints one file per line:

pattern=/tmp/????? ; for f in $pattern ; do if [ $f != "$pattern" ] ; then echo $f ; fi ; done

How about:

ls /tmp | grep -e '^.\{4\}$'    

for a grep based answer.

  • 1
    This won't work if filename contains newline – cuonglm Jun 4 '15 at 17:08
  • @cuonglm if your going to be pedantic about newlines, at least apply it evenly across all answers to a question. Any globbing with ?(s) is susceptible to this defect. – joe Lovick Jun 4 '15 at 17:30
  • 1
    @joeLovick: You created file with literal \n, not newline – cuonglm Jun 4 '15 at 17:55
  • 3
    never parse ls for anything – frostschutz Jun 5 '15 at 7:43
  • 1
    ls -q | grep -E '^.{4}$' should take care of those file names with funky chars (though they'll appear as question marks in the output). – don_crissti Jun 5 '15 at 9:46

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