Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

In bash, how do I specify a pattern that matches everything but subdirectories in current directory. Given that the pattern */ matches all the subdirectories, I tried (with extglob turned on):

$ echo !(*/)

But it didn't work.

share|improve this question

5 Answers 5

up vote 6 down vote accepted
find . -maxdepth 1 ! -type d

Details:

  • -maxdepth 1 restricts the search to the current directory

  • ! -type d eliminates directories

share|improve this answer
    
Great simple solution. Thanks. –  York Jul 13 at 22:02
    
another variant that i use frequently is find . -maxdepth 1 -type f this only matches regular files –  hildred Jul 14 at 12:42

The reason */ matches directories is that the final / restricts matches to directories. This effect is only triggered when the / is after a pattern, you can't use / inside parentheses in !(*/). There's no feature built into bash to do what you want.

You can make a loop over all files and build an array.

non_directories=()
for x in *; do
  [ -d "$x" ] || non_directories+=("$x")
done
somecommand "${non_directories[@]}"

You can also use find, if you want to execute a command over all files. If your find implementation supports it (GNU, BSD, BusyBox), use -mindepth and -maxdepth to list only entries in the current directories (with ./ prepended). Use ! -name '.*' to omit dot files (if you indeed want to omit them).

find . -mindepth 1 -maxdepth 1 ! -type d -exec somecommand {} +

If you can only assume a POSIX find, use -prune to avoid recursing.

find . -name . -o -type d -prune -o -exec somecommand {} +

If you want to stuff the output of find into an array variable, beware that parsing the output of find requires additional assumptions on the file name. You're better off with a loop.

In zsh, you can use glob qualifiers: *(^/), or *(.) to match only regular files.

share|improve this answer
    
Very informative answer. Thanks. –  York Jul 13 at 22:05
    
What does the -mindepth 1 do in your first example? You are already excluding directories with the ! -type d, is there any point to using -mindepth as well? Also, your -prune example won't work, did you mean -type f? –  terdon Jul 14 at 12:00
    
@terdon I included -mindepth 1 as a force of habit for a non-recursive use of find. I guess it doesn't hurt and if I left it out one could arguably wonder why. The second find command was a typo (braino?) of mine: -name . has to go first, otherwise . would end up pruned. –  Gilles Jul 14 at 14:23

Sort of long winded, but:

for f in *; do if [ ! -d "$f" ]; then echo "$f"; fi; done

-d is a file test operator to check if the argument is a directory.

The above could also be shortened to

for f in *; do [ ! -d "$f" ] && echo "$f"; done
share|improve this answer
    
Correct answer and the link provided was really nice. Thanks. –  York Jul 13 at 22:03

This should really be a comment, but I don't have enough points. That said - I'm not sure which of the 2 options to you want to achieve - bash globing that excludes based on filesystem attributes, or a way to display only files in a directory?

If the first, I'm not sure that's possible. Globing just expands, and filenames are a base in any directory - bash doesn't distinguish between a file name or a dirname on it's own.

If the second - you already got 2 answers, another one using ls:

ls -dF * | grep -v "/$"  
share|improve this answer

My suggestion:

GLOBIGNORE=$(echo */)            # create list of directories with globbing
GLOBIGNORE=${GLOBIGNORE//:/\\:}  # escape possible ":" with "\" to allow
                                 # the separator ":" in directory names
GLOBIGNORE=${GLOBIGNORE//\/ /:}  # replace "/ " with separator ":" 
GLOBIGNORE=${GLOBIGNORE%/}       # remove trailing "/"
ls -ld *

Result: no directories

Back to default with unset GLOBIGNORE

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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