So, ls -R
is a command I use a lot to quickly verify the file structure of my projects, or check for any leftover files, as it's simple enough to use and read, and is part of coreutils (which means I don't need to install anything, as it's already available on all distros).
However, lately, my projects have grown more complex, having more and more files and sub-directories, and sub-directories of sub-directories, which I don't care about at all. Using ls -RI "PATTERN"
used to work well, but now I'm facing an issue where there are files matching a specific pattern that I want to ignore in one specific directory, but not in the other, and vice versa.
The problem is that ls -I "PATTERN"
seems to look for the shell PATTERN in name of the file, rather than its full path, which makes it impossible to do something like ls -R "subdir/file.a"
, as the name of the file I'm trying to ignore here is "file.a", and not "subdir/file.a" (which is its path).
In short, is there any way to make ls -I
look for the pattern in the full path of the file, instead of only its name, or is there any coreutils equivalent command that would allow me to do that without losing much human readability?
Here's a quick example to illustrate my problem:
File structure:
. ├── subdir1 │ ├── file.a │ └── file.b └── subdir2 ├── file.a └── file.b
What I want:
.: subdir1 subdir2 ./subdir1: file.a ./subdir2: file.b
What I get with
ls -ARIsubdir1/file.b -Isubdir2/file.a
:.: subdir1 subdir2 ./subdir1: file.a file.b ./subdir2: file.a file.b
2 Answers 2
While ls
can't do it, find
can. And while find isn't coreutils, it is generally available.
$ find . -path ./subdir1/file.b -o -path ./subdir2/file.a -o -print
.
./subdir2
./subdir2/file.b
./subdir1
./subdir1/file.a
$
In any case, you will probably want to pipe it through sort.
There are multiple techniques for ignoring things through find.
You might be better off making a script to run find
, then sort
, then sed
(or other tools) to delete things you don't want shown.
-
2GNU coreutils and GNU findutils are just two bundles of basic command line software for the GNU system. coreutils merges what used to be sh-utils (formerly shellutils) fileutils and textutils, all of which include GNU implementations of standard POSIX utilities such as
find
as well as some more non-standard utilities (such aslocate
also found infindutils
). In any case, GNU systems will have thefind
from GNU findutils like they'll havels
from GNU coreutils, or GNUsed
orgrep
which are in separate packages. More importantlyfind
likels
is a standard POSIX commands.Stéphane Chazelas– Stéphane Chazelas2025年06月22日 20:40:33 +00:00Commented Jun 22 at 20:40 -
1@StéphaneChazelas right. but OP raised about coreutils, and liking that its already available. find (one of two tools in findutils) is much the same: available. Which I said without going into painful details.David G.– David G.2025年06月22日 20:44:58 +00:00Commented Jun 22 at 20:44
-
(though
ls -I
or you not giving any path forfind
to find in are GNU extensions; changingfind -path...
tofind . -path...
would make it standard)Stéphane Chazelas– Stéphane Chazelas2025年06月22日 20:45:03 +00:00Commented Jun 22 at 20:45 -
Yes, my initial comment was more for the OP who may be confused as to what "coreutils" is.Stéphane Chazelas– Stéphane Chazelas2025年06月22日 20:45:51 +00:00Commented Jun 22 at 20:45
-
1To get a sorted list of file paths, that would have to be something like
find . -path ./subdir1/file.b -o -path ./subdir2/file.a -o -print0 | sort -z | tr '0円' '\n'
(withsort -z
being a GNU extension). Sorting the output offind -print
would break file paths that contain newline characters.Stéphane Chazelas– Stéphane Chazelas2025年06月22日 20:47:12 +00:00Commented Jun 22 at 20:47
With zsh
:
set -o extendedglob
print -rC1 -- **/*~(subdir2/file.a|subdir1/file.b)(N)
Would print
r
aw on 1
C
olumn the sorted list of all the non-hidden files excluding subdir2/file.a
and subdir1/file.b
. To also exclude any file under subdir2/file.a
and subdir1/file.b
in case those are of type directory (like ls -RI file.a -I file.b
would), you can change it to:
print -rC1 -- **/*~(subdir2/file.a|subdir1/file.b)(|/*)(N)
(though note that the ~
operator to exclude files applies after directory traversal, so zsh
would still look into those directories recursively even if it wouldn't print the corresponding paths).
With the globstartshort
option enabled, **/*
can be shortened to **
.
You can replace print -rC1
with ls -ld
to get a long listing for those files.
A GNU find
+ GNU sort
equivalent could look like:
LC_ALL=C find . -mindepth 1 '(' \
-name '.?*' -o -path ./subdir2/file.a -o -path ./subdir1/file.b \
')' -prune -o -printf '%P0円' | sort -z | tr '0円' '\n'
Or to pass to ls -l
:
LC_ALL=C find . -mindepth 1 '(' \
-name '.?*' -o -path ./subdir2/file.a -o -path ./subdir1/file.b \
')' -prune -o -printf '%P0円' | sort -z | xargs -r0 ls -ld --
ls *
does not mean to pass*
tols
, same goes with any other wildcard pattern. Instead, the shell first replaces*
before passing the resulting arguments tols
(this is called globbing). Instead,ls '*'
(with quotes) would pass*
, butls
won't handle this,find
is the right tool you are looking for.