2

Is it possible to achieve the following with a simple ls command using bash. List all the files that begin with two letters, have an e in the name and end with 1 or more letter. If so, what is the command and does extended globbing need to be enabled ?

damiant
  • 31

1 Answers1

5

ls doesn't match file names (unless you want to consider GNU extensions like --include/--exclude). It's the shell globs that are typically used for that.

ls -ld [[:alpha:]][[:alpha:]]*e*[[:alpha:]]

would find files that match those requirements, but would miss ee for instance. You could do:

zsh

Using the AND NOT/EXCEPT operator (~) and NOT (^):

setopt extendedglob
ls -ld [[:alpha:]](#c2)*~^*e*~^*[[:alpha:]]

Or using perl-like look ahead regular expression operators:

setopt rematchpcre
match() [[ $REPLY =~ '^(?=.*e)(?=.*[[:alpha:]]$)[[:alpha:]][[:alpha:]]' ]]
ls -ld *(+match)

ksh93

Using & inside @(...):

ls -ld @([[:alpha:]][[:alpha:]]*&*e*&*[[:alpha:]])

Same with its augmented regexps (enabled with ~(X:...)):

ls -ld ~(X:[[:alpha:]][[:alpha:]].*&.*e.*&.*[[:alpha:]])

Or using perl-like look-ahead operators:

ls -ld ~(P:(?=.*e)(?=.*[[:alpha:]]$)[[:alpha:]][[:alpha:]])*

bash

bash has no AND operator but it does support a subset of ksh's operators when the extglob option is enabled, so you could do things like:

shopt -s extglob
ls -ld @([[:alpha:]]?([[:alpha:]]*)e?(*[[:alpha:]])|e[[:alpha:]]?(*[[:alpha:]]))

@(x|y) (like zsh's (x|y)) is a OR operator. ?(x) is an optional x (like zsh's (x|); @(x|) would also work for that in ksh/bash).

Another trick is to use the fact that "A and B" is "not(not(A) or not(B))", and the subset of ksh globs supported by bash so happens to include a not and or operators so you can do:

ls -ld !(!([[:alpha:]][[:alpha:]]*)|!(*e*)|!(*[[:alpha:]]))

Those ones would also work in ksh (all variants, no shopt there) where those operators come from in the first place.

Non-glob-based approaches

With find:

find . ! -name . -prune -name '[[:alpha:]][[:alpha:]]*' \
                        -name '*e*' \
                        -name '*[[:alpha:]]' -exec ls -ld {} +

(beware the file list won't be sorted and file names containing bytes that don't form valid characters will be excluded; file names will also be prefixed with ./).

  • just trying that command now and it doesnt appear to work ? – damiant Oct 06 '17 at 19:22
  • @damiant, which command? In which shell? And in which way does it not work? – Stéphane Chazelas Oct 06 '17 at 19:35
  • the bash command. i was trying with ls | grep “^[a-zA-Z][a-zA-Z].e.[a-zA-Z]$” but that wont work if either of the first 2 characters are e – damiant Oct 06 '17 at 20:32
  • @damiant what file name does that ls -ld @([[:alpha:]]?([[:alpha:]]*)e?(*[[:alpha:]])|e[[:alpha:]]?(*[[:alpha:]])) bash command fail to match or fail to not-match. For me it correctly matches ee, eea, ee1a and correctly fails to match ee1, eee1 and ee11 – Stéphane Chazelas Oct 06 '17 at 21:03