oniramarf
oniramarf

Reputation: 903

Bash: loop through files that DO NOT match extension

I'm writing a bash script that needs to loop files inside a directory that do not match a specific extension. So far, I've found that the following code loops all files that matches the given extension:

for f in *.txt ; do
    echo $f;
done

How could insthead loop through files that do not match the specified extension?

Upvotes: 11

Views: 7723

Answers (6)

anubhava
anubhava

Reputation: 785256

to loop files inside a directory that do not match a specific extension

You can use extglob:

shopt -s extglob

for f in *.!(txt); do
    echo "$f"
done

pattern *.!(txt) will match all entries with a dot and no txt after the dot.


EDIT: Please see comments below. Here is a find version to loop through files in current directory that don't match a particular extension:

while IFS= read -d '' -r f; do
    echo "$f"
done < <(find . -maxdepth 1 -type f -not -name '*.txt' -print0)

Upvotes: 6

Jay jargot
Jay jargot

Reputation: 2868

If you are ok for a GNU solution, give a try to this:

for f in $(find . -maxdepth 1 -type f \! -name \*.txt) ; do
  printf "%s\n" "${f}"
done

This is going to break if special chars are contained in the filenames, such as (space).

For something safe, still GNU, try:

find . -maxdepth 1 -type f \! -name \*.txt -printf "%p\0" | xargs -0 sh -c '
    for f ; do
      printf "%s\n" "${f}"
    done' arg0

Upvotes: 0

Jahid
Jahid

Reputation: 22428

This will do:

shopt -s extglob
for f in !(*.txt) ; do
    echo $f
done

You just inverse the glob pattern using !(glob_pat), and to use it, you need to enable extended glob.

If you want to ignore directories, then:

shopt -s extglob
for f in !(*.txt) ; do
    [ -d "$f" ] && continue   # This will ignore dirs
    # [ -f "$f" ] && continue # This will ignore files
    echo $f
done

If you wanna go into all sub-dirs then:

shopt -s extglob globstar
for f in !(*.txt) **/!(*.txt) ; do
    [ -d "$f" ] && continue   # This will ignore dirs
    # [ -f "$f" ] && continue # This will ignore files
    echo $f
done

Upvotes: 3

sjsam
sjsam

Reputation: 21965

Do

find /path/to/look -type f -not -name "*.txt" -print0 | while read -r -d '' file_name
do
echo "$file_name"
done

when your filenames may be nonstandard.

Note:

If you don't wish to recursively search for files in subfolders include -maxdepth 1
just before -type f.

Upvotes: 1

chepner
chepner

Reputation: 531275

You can pattern-match with the == operator.

for f in *; do
    [[ $f == *.txt ]] && continue
    # [[ $f != *.txt ]] || continue
    ...
done

If this might run in an empty directory, either use shopt -s nullglob prior to the loop, or put [ -e "$f" ] || continue in side the loop. (The former is preferable, as it avoids constantly checking if a file exists.)

Upvotes: 16

Kevin Aud
Kevin Aud

Reputation: 398

for f in $(ls --hide="*.txt")
do
    echo $f
done

Upvotes: -2

Related Questions