Let's say I have a directory with files a1, a2, a3, b1, b2, b3. I only want to match files that start with a
but don't contain 3
. I tried ls -I "*3" *a*
but it returns a1 a2 a3
, even though I don't want it to match a3
. Is this possible with ls
?
5 Answers
Just:
shopt -s extglob failglob
ls -d a!(*3*)
shopt -s extglob
activates extended globbing (enables a subset of ksh's extended operators).failglob
it to cancel the command if there's no match rather than calling it with the glob pattern left as is. Without it, that could end up list the file calleda!(*3*)
even though it contains a3
.a
matches the startinga
!()
negates the match inside the()
...*3*
which is3
and anything before or after it.
$ touch 1 2 3 a1 a2 a3 b1 b2 b3 aa1 aa2 aa3 a2a a3a
$ ls -d a!(*3*)
a1 a2 a2a aa1 aa2
Existing answers well state the best way to do this with globs; I'll include this because in most cases, find
is a better option if you want to do scripting or further processing:
$ find . -maxdepth 1 -name 'a*' -not -name '*3*'
./a1
./a2
You can feed the answer to xargs
for easy actions, or to a while read
loop for more complicated things.
-
See
!
for the standard equivalent to-not
. Note that in order to be able to feed it to xargs or read, you need to add a-print0
and usexargs -r0
orwhilte IFS= read -rd '' file
(for which you need zsh or bash) Commented Mar 17, 2023 at 12:40
An easy way is
- to list all files that match
a*
- then delete all lines containing a
3
The UNIX command is
ls -1 a* | grep -v 3
ls -1
shows one file per line.
grep
normally finds lines that match a pattern but with -v
this behaviour is inverted, i.e. grep finds line that don't match that pattern.
-
2Hi Peter! It would be best to explain, in as much detail as is relevant, how your solution works.– dhagCommented Oct 21, 2015 at 19:35
-
3Parsing
ls
is not a goob idea: mywiki.wooledge.org/ParsingLs– chaosCommented Oct 21, 2015 at 19:38
With zsh
, using the ~
glob operator:
setopt extendedglob
print -rl -- a*~*3*
With gnu
ls
you could use two --ignore
options:
ls -I '[^a]*' -I '*3*'
the 1st ignores all file names that don't start with a
and the 2nd ignores file names that contain 3
As answers before suggest: Yes, it is possible to solve this with ls. To me, this particular question also somewhat implies why your solution has not worked out. Therefore, I'd like to give a late contribution on that.
Basically,
ls -I "*3" *a*
asks to exclude all entries ending in 3, but in turn includes explicitly all files containing an a. In fact, *a* uses shell globbing and is expanded before the actual command is executed (this does not apply to "*3" as this part is quoted). Thus, your command is in your case equivalent to
ls -I "*3" a1 a2 a3
consequently listing the entry a3. Thus, explicit given entries to be listed are overruling entries concerned by -I. From there, I derive, implicitly in the sense of the manual means everything not specified explicitly, e.g.
ls -I "*3"
would have excluded a3 (but shown other considered entries in that directory too, e.g. b1).
-
Hey @Peregrino69, thanks for your remark. As you figured, I cannot yet comment. On the other side, I found, this explanation is an important addition to answering the question. I have precised this now. Commented Mar 17, 2023 at 10:40
-
Agreed mate :-) However Q&A sites work differently from discussion forums. An answer should be self-contained; i.e. if everyone else decides to delete theirs, the OP (or anyone stumbling upon this via web search) should be able to resolve the issue just with your answer alone. Same logic applies here as with links to external sources - link-only answers do get deleted. Check the Tour and the Asking and Answering -sections in the Help center; that'll give a good understanding on how these sites work. Commented Mar 17, 2023 at 11:10
do not list implied entries matching shell PATTERN
, however it doesn't explain what is meant by implied, but I'm guessing it only applies to files thatls
gets by glob expansions - andls
doesn't expand thea*
, the shell does that.