• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

contrib/H18-Jan-2020-4122

LICENSEH A D18-Jan-20201.2 KiB2620

MakefileH A D18-Jan-2020482 2417

NEWS.mdH A D18-Jan-20202.6 KiB8358

README.mdH A D18-Jan-202010.5 KiB327268

_lrH A D18-Jan-20202.9 KiB107102

lr.1H A D18-Jan-20208.7 KiB438424

lr.cH A D18-Jan-202054.3 KiB2,6082,263

README.md

1## lr: list files, recursively
2
3`lr` is a new tool for generating file listings, which includes the
4best features of `ls(1)`, `find(1)`, `stat(1)` and `du(1)`.
5
6`lr` has been tested on Linux 4.1, FreeBSD 10.2, OpenBSD 5.7,
7NetBSD 5.2.3, DragonFlyBSD 5.0, Mac OS X 10.10, OmniOS 5.11 and Cygwin 1.7.32.
8It will likely work on other Unix-like systems with C99, but you'll
9need to port `scan_filesystems` for `fstype` to work.
10
11## Screenshot
12
13![Screenshot of lr -AFGl -ovh](lr.png)
14
15## Benefits
16
17Over find:
18* friendly and logical C-style filter syntax
19* getopt is used, can mix filters and arguments in any order
20* can sort
21* compute directory sizes
22* can strip leading `./`
23* can do breadth first search
24
25Over ls:
26* sorts over all files, not per directory
27* copy & paste file names from the output since they are relative to pwd
28* ISO dates
29* powerful filters
30
31## Rosetta stone
32
33* `ls`: `lr -1 | column`
34* `find .`: `lr` (or `lr -U` for speed.)
35* `ls -l`: `lr -1l`
36* `ls -ltrc`: `lr -l1Aoc`
37* `find . -name '*.c'`: `lr -t 'name ~~ "*.c"'`
38* `find . -regex '.*c'`: `lr -t 'path =~ "c$"'`
39* `find -L /proc/*/fd -maxdepth 1 -type f -links 0 -printf '%b %p\n'`:
40`lr -UL1 -t 'type == f && links == 0' -f '%b %p\n' /proc/*/fd`
41* `find "${@:-.}" -name HEAD -execdir sh -c 'git rev-parse --resolve-git-dir . >/dev/null 2>/dev/null && pwd' ';'`: `lr -0U -t 'name == "HEAD"' "$@" | xe -0 -s 'cd ${1%/*} && git rev-parse --resolve-git-dir . >/dev/null && pwd; true' 2>/dev/null`
42* Filter list of files for existence: `xe lr -dQU <list`
43* replacement for who(1): `lr -om -t 'name =~ "[0-9][0-9]*$" && uid != 0' -f '%u\t%p\t%CY-%Cm-%Cd %CH:%CM\n' /dev/pts /dev/tty*`
44* Find files with setuid or setgid: `lr -t 'mode | 06000' /usr/bin` or `lr -t 'mode = "u+s" || mode = "g+s"' /usr/bin`
45* Find files with non-umask permissions: `lr -t '!(mode = "=rw,+X")' -l`
46* Find broken symlinks: `lr -L -t 'type = l'`
47
48## Usage:
49
50	lr [-0|-F|-l [-TA|-TC|-TM]|-S|-f FMT] [-B|-D] [-H|-L] [-1AGPQXdhsx] [-U|-W|-o ORD] [-e REGEX]* [-t TEST]* PATH...
51
52The special path argument `-` makes `lr` read file names from standard
53input, instead of traversing path.
54
55* `-0`: output filenames separated by NUL bytes.
56  Likewise, read input filenames separated by NUL bytes.
57* `-F`: output filenames and an indicator of their file type (`*/=>@|`).
58* `-l`: long output a la `ls -l` (implies `-Q`).
59* `-TA`: with `-l`, output atime.
60* `-TC`: with `-l`, output ctime.
61* `-TM`: with `-l`, output mtime (default).
62* `-S`: BSD stat(1)-inspired output (implies `-Q`).
63* `-f FMT`: custom formatting, see below.
64* `-B`: breadth first traversal.
65* `-D`: depth first traversal. `prune` does not work, but `entries`
66  and `total` are computed on the fly.
67* `-H`: only follow symlinks on command line.
68* `-L`: follow all symlinks.
69* `-1`: don't go below one level of directories.
70* `-A`: don't list files starting with a dot.
71* `-G`: colorize output to tty.  Use twice to force colorize.
72* `-X`: print OSC 8 hyperlinks to tty.  Use twice to force.
73* `-P`: quote file names using `$'...'` syntax.
74* `-Q`: shell quote file names (default for output to TTY).
75* `-d`: don't enter directories.
76* `-h`: print human readable size for `-l` (also `%s`).
77* `-s`: strip directory prefix passed on command line.
78* `-x`: don't enter other filesystems.
79* `-U`: don't sort results, print during traversal.
80* `-W`: sort results by name and print during traversal.
81* `-o ORD`: sort according to the string `ORD`, see below.
82* `-e REGEX`: only show files where basename matches `REGEX`.
83* `-t TEST`: only show files matching all `TEST`s, see below.
84
85## Output formatting:
86
87* `\a`, `\b`, `\f`, `\n`, `\r`, `\v`, `\0` as in C.
88* `%%`: plain `%`.
89* `%s`: file size in bytes.
90* `%S`: file size, with human readable unit.
91* `%b`: file size in 512-byte blocks.
92* `%k`: file size in 1024-byte blocks.
93* `%d`: path depth.
94* `%D`: device number (`stat.st_dev`).
95* `%R`: device ID for special files (`stat.st_rdev`).
96* `%i`: inode number.
97* `%I`: one space character for every depth level.
98* `%p`: full path (`%P` if `-s`).
99* `%P`: full path without command line argument prefix.
100* `%l`: symlink target.
101* `%n`: number of hardlinks.
102* `%F`: file indicator type symbol (`*/=>@|`).
103* `%f`: file basename (everything after last `/`).
104* `%A-`, `%C-`, `%T-`: relative age for atime/ctime/mtime.
105* `%Ax`, `%Cx`, `%Tx`: result of `strftime` for `%x` on atime/ctime/mtime.
106* `%m`: octal file permissions.
107* `%M`: ls-style symbolic file permissions.
108* `%y`: ls-style symbolic file type (`bcdfls`).
109* `%g`: group name.
110* `%G`: numeric gid.
111* `%u`: user name.
112* `%U`: numeric uid.
113* `%e`: number of entries in directories.
114* `%t`: total size used by accepted files in directories (only with `-D`).
115* `%Y`: type of the filesystem the file resides on.
116* `%x`: Linux-only: a combination of: `#` for files with security capabilities, `+` for files with an ACL, `@` for files with other extended attributes.
117
118## Sort order
119
120Sort order is string consisting of the following letters.
121Uppercase letters reverse sorting.
122E.g. `Sn` sorts first by size, smallest last, and then by name (in
123case sizes are equal).
124
125Default: `n`.
126
127* `a`: atime.
128* `c`: ctime.
129* `d`: path depth.
130* `e`: file extension.
131* `i`: inode number.
132* `m`: mtime.
133* `n`: file name.
134* `p`: directory name.
135* `s`: file size.
136* `t`: file type.  This sorts all directories before other files.
137* `v`: file name as version numbers (sorts "2" before "10").
138
139## Filter expressions
140
141`lr` filters are given by the following EBNF:
142
143	<expr>     ::= <expr> || <expr>  -- disjunction
144	             | <expr> && <expr>  -- conjunction
145	             | <expr> ? <expr> : <expr>  -- ternary operator
146	             | ! <expr>          -- negation
147	             | ( <expr )
148	             | <timeprop> <numop> <dur>
149	             | <numprop> <numop> <num>
150	             | <strprop> <strop> <str>
151	             | <typetest>
152	             | <modetest>
153	             | prune             -- do not traverse into subdirectories
154	             | print             -- always true value
155	             | skip              -- always false value
156	             | color <num>       -- always true value, override 256-color
157
158        <timeprop> ::= atime | ctime | mtime
159
160	<numprop>  ::= depth | dev | entries | gid | inode
161	             | links | mode | rdev | size | total | uid
162
163	<numop>    ::= <= | < | >= | > | == | = | !=
164
165        <dur>      ::= "./path"          -- mtime of relative path
166                     | "/path"           -- mtime of absolute path
167                     | "YYYY-MM-DD HH:MM:SS"
168                     | "YYYY-MM-DD"      -- at midnight
169                     | "HH:MM:SS"        -- today
170                     | "HH:MM"           -- today
171                     | "-[0-9]+d"        -- n days ago at midnight
172                     | "-[0-9]+h"        -- n hours before now
173                     | "-[0-9]+m"        -- n minutes before now
174                     | "-[0-9]+s"        -- n seconds before now
175                     | [0-9]+            -- absolute epoch time
176
177	<num>      ::= [0-9]+ ( c        -- *1
178	                      | b        -- *512
179	                      | k        -- *1024
180	                      | M        -- *1024*1024
181	                      | G        -- *1024*1024*1024
182	                      | T )?     -- *1024*1024*1024*1024
183
184	<strprop>  ::= fstype | group | name | path | target | user | xattr
185
186	<strop>    ::= == | = | !=       -- string (in)equality
187	             | ===    | !===     -- case insensitive string (in)equality
188	             | ~~     | !~~      -- glob (fnmatch)
189	             | ~~~    | !~~~     -- case insensitive glob (fnmatch)
190	             | =~     | !=~ | !~ -- POSIX Extended Regular Expressions
191	             | =~~    | !=~~     -- case insensitive POSIX Extended Regular Expressions
192
193	<str>      ::= " ([^"] | "")+ "  -- use "" for a single " inside "
194	             | $[A-Za-z0-9_]     -- environment variable
195
196	<typetest> ::= type ( == | = | != ) ( b | c | d | p | f | l )
197
198	<modetest> ::= mode ( == | =     -- exact permissions
199	                    | &          -- check if all bits of <octal> set
200	                    | |          -- check if any bit of <octal> set
201	                    ) <octal>
202	             | mode = "<chmod>"  -- check if symbolic mode is satisfied
203
204	<octal> ::= [0-7]+
205
206	<chmod> ::= <clause> (, <clause>)+
207
208	<clause> ::= [guoa]* [+-=] [rwxXstugo]*  -- see chmod(1)
209
210## EWONTFIX
211
212The following features won't be implemented:
213
214* `-exec`: use `-0` and `xargs`
215  (or even better [xe](https://github.com/leahneukirchen/xe)).
216* columns: use `column`, `git-column` (supports colors), Plan 9 `mc`.
217  (e.g. `lr -1AGFs | git column --mode=dense --padding=2`)
218
219## "Screenshots"
220
221Default output, sorted by name:
222
223```
224% lr
225.
226.git
227.git/HEAD
228.git/config
229[...]
230Makefile
231README.md
232lr.c
233```
234
235Long output format:
236
237```
238% lr -l
239drwxrwxr-x 3 chris users   120 2015-10-27 13:56 ./
240drwxrwxr-x 7 chris users   240 2015-10-27 13:56 .git/
241-rw-rw-r-- 1 chris users    23 2015-10-27 13:56 .git/HEAD
242-rw-rw-r-- 1 chris users   257 2015-10-27 13:56 .git/config
243[...]
244-rw-rw-r-- 1 chris users   297 2015-10-27 13:56 Makefile
245-rw-rw-r-- 1 chris users  5828 2015-10-27 13:56 README.md
246-rw-rw-r-- 1 chris users 27589 2015-10-27 13:56 lr.c
247```
248
249Simple test:
250
251```
252% lr -F -t 'type == d'
253./
254.git/
255.git/hooks/
256.git/info/
257.git/logs/
258.git/logs/refs/
259.git/logs/refs/heads/
260.git/logs/refs/remotes/
261.git/logs/refs/remotes/origin/
262.git/objects/
263.git/objects/info/
264.git/objects/pack/
265.git/refs/
266.git/refs/heads/
267.git/refs/remotes/
268.git/refs/remotes/origin/
269.git/refs/tags/
270```
271
272List regular files by size, largest first:
273
274```
275% lr -f '%S %f\n' -1 -t 'type == f' -oS
276  27K lr.c
277 5.7K README.md
278  297 Makefile
279```
280
281List directory total sizes, indented:
282
283```
284% lr -D -t 'type == d' -f '%I%I%t %p\n'
285172 .
286  132 .git
287    40 .git/hooks
288    4 .git/info
289    12 .git/logs
290      8 .git/logs/refs
291        4 .git/logs/refs/heads
292        4 .git/logs/refs/remotes
293          4 .git/logs/refs/remotes/origin
294    48 .git/objects
295      0 .git/objects/info
296      48 .git/objects/pack
297    8 .git/refs
298      4 .git/refs/heads
299      4 .git/refs/remotes
300        4 .git/refs/remotes/origin
301      0 .git/refs/tags
302```
303
304List all files, but print them in red if they match "havoc":
305
306```
307% lr -G -t 'name =~ "havoc" && color 160 || print'
308```
309
310Do not enter `.git` or `.hg` directories:
311
312```
313% lr -t 'name = ".git" || name = ".hg" ? prune : print' .
314```
315
316## Installation
317
318Use `make all` to build, `make install` to install relative to `PREFIX`
319(`/usr/local` by default).  The `DESTDIR` convention is respected.
320You can also just copy the binary into your `PATH`.
321
322## Copyright
323
324Copyright (C) 2015-2020 Leah Neukirchen <purl.org/net/chneukirchen>
325
326Licensed under the terms of the MIT license, see lr.c.
327