The uvp command — a manual for investigating huge files from the CLI

The uvp command — manual

UwView Pro v1.6.0 ships uvp, a command you can call from a terminal. It runs the same engine and reads the same .uwvz as the GUI, so whatever you were looking at on screen, you can now pull from a script.

This page lists every command, pairs each one with its rg (ripgrep) / sed / sort / uniq equivalent, and gives measured numbers.

Three things make uvp worth having.

  1. Past 10 GB it is several times faster than rg — 7.65× on a 50 GB search, 7.89× on a tally, and up to 24.18× against sed (second run; measurements from §4). The boundary is not the file size but the second question: rg re-reads the whole file for every question, uvp keeps an index
  2. You can move from the CLI into the GUI mid-investigation — just add -open. The same .uwvz is reused, so nothing is rebuilt (§6)
  3. Once you are on screen, you can keep asking — click a row and it becomes the next stage. Stages after the first only look inside the previous stage, so they come back immediately

About the numbers (please read)
Every timing on this page is a second run — the .uwvz already exists (hot). Each item was measured as syncsudo purge → 10 s wait → cold → hot immediately after, and rg is compared on the same hot (second) run. uvp builds the sidecar on the first run (3 GB 1.2 s, 10 GB 13.7 s, 50 GB 66 s — roughly 1.3 s/GB). For the first-run cost and the cold figures, see I gave UwView Pro a command line and the benchmarks page.
Setup: Mac M4, 32 GB RAM, external USB SSD, ripgrep 15.2.0, BSD sed, uvp 1.6.3 (17 September 2026). Data: OpenStreetMap Japan (XML) at 3 GB / 10 GB / 50 GB.
Every result was compared line by line against rg / sed78 checks, all matching, zero mismatches.


1. Install and put it on your PATH

After installing UwView Pro, open the app menu Help → “Command line setup…”.

OS Where it goes
macOS /usr/local/bin/uvp (you may be asked for your password)
Windows adds a folder to your user PATH (no admin rights needed)
Linux /usr/local/bin, falling back to ~/.local/bin (log in again to pick it up)
uvp --version     # uvp 1.6.3
uvp --help        # every command

The free UwView ships uvf the same way (search, -i, -E, -v, -open; no index). It is about as fast as rg: a 50 GB search takes 50.7 s against rg’s 55.4 s.


2. Three commands to start with

# 1. Find (same lines as rg -n -F)
uvp huge.log 'ERROR'

# 2. Count (instead of sort | uniq -c | sort -rn | head)
uvp huge.log 'ERROR' -uniq 'code=([0-9]+)' -head 20

# 3. Replace (instead of sed 's/A/B/g'; the file itself is never modified)
uvp huge.log 'old-host' -replace 'new-host' -out fixed.log

3. The shape of a command

uvp <file> <term> [stages…] [options]        investigate (flag syntax)
uvp <file> '<term> | sort … | uniq …'        quoted pipe syntax (same meaning)
uvp convert <file> [-out x.uwvz]             build the .uwvz
uvp cat <file> [-out …]                      print the text
uvp <file.uwvz> -extract [txt|gz|zip]        restore the original (free)
uvp -open [<file>]                           launch the app

Stages run in the order you write them, exactly like a pipeline. -uniq then -sort count is not the same as -sort then -uniq.


4. Stages — and how you would write them in the CLI

What you want CLI uvp
Literal rg -n -F 'Tokyo' F uvp F 'Tokyo'
Ignore case rg -n -i -F tokyo F uvp F tokyo -i
Regex rg -n 'v="(a\|b)"' F uvp F 'v="(a\|b)"' -E
Whole word rg -n -w 'id' F uvp F 'id' -w

Measured (second run, .uwvz reused)

Hits (50 GB) 3 GB 10 GB 50 GB
Literal 東京 94,979 rg 0.33 s / uvp 1.04 s (rg wins by 3.15×) rg 11.23 / uvp 1.80 (6.24×) rg 56.72 / uvp 7.41 (7.65×)
Ignore case -i (HIGHWAY) 11,693,479 rg 0.62 / uvp 1.23 (rg wins by 1.98×) rg 11.98 / uvp 1.90 (6.31×) rg 56.59 / uvp 16.02 (3.53×)
Regex -E (k="highway[^"]*") 11,529,957 rg 0.53 / uvp 1.33 (rg wins by 2.51×) rg 10.94 / uvp 1.87 (5.85×) rg 55.50 / uvp 15.03 (3.69×)
Anchored regex ^ *<tag k="name" 3,337,643 rg 0.48 / uvp 1.09 (rg wins by 2.27×) rg 11.05 / uvp 1.95 (5.67×) rg 55.78 / uvp 7.04 (7.92×)
Non-matching lines -v 48,984,614 rg 2.10 / uvp 3.61 (rg wins by 1.72×) rg 11.09 / uvp 1.99 (5.57×) rg 57.78 / uvp 29.15 (1.98×)

On a 3 GB file already in the cache, rg wins the second run in every form (uvp always pays 0.6–1.0 s of start-up and index loading). On the same 3 GB file cold, uvp is 2.2–2.9× faster on -i and -E, and on the two-run total (cold + hot) it wins by 1.3–1.7×. From 10 GB up uvp is 4.3–6.3× even hot; at 50 GB the ratio drops on -i, -E and -v, which return more than 10 million lines (uvp holds the hits). A regex costs what its hit count costs, not what its shape costs (50 GB: anchored regex 3.34 M hits 7.04 s, literal 95 K hits 7.41 s).

4-2. Narrowing (drill-down) — just add another term

# CLI: chain pipes
rg -n -F 'k="amenity"' F | rg -F 'v="parking"'
rg -n -F 'k="amenity"' F | rg -v -F 'v="parking"'     # exclude

# uvp: list the terms. Later terms search inside the previous stage's ±N window
uvp F 'k="amenity"' 'v="parking"'
uvp F 'k="amenity"' 'v="parking"' -v                  # exclude
uvp F 'k="amenity"' -C 3 'k="name"'                   # 2nd term within 3 lines

Measured (two-term narrowing, second run): 3 GB rg 0.46 s / uvp 1.04 s (rg wins by 2.26×); 10 GB rg 10.94 / uvp 1.77 (6.18×); 50 GB rg 56.09 / uvp 6.41 (8.75×). Since v1.6.2 each later stage looks only at the previous stage’s results, so a stage costs 0.24 s even at 50 GB.

-C N does not mean “print N lines of context” — it means “keep only the hits that have the next term within N lines“. rg -C merely prints context, so there is no single CLI command with this meaning.

4-3. Tally

# CLI
rg -N -o 'k="amenity" v="([^"]+)"' -r '$1' F | LC_ALL=C sort | uniq -c | sort -k1,1nr | head -20

# uvp (the key is the first capture group of a regex)
uvp F 'k="amenity"' -uniq 'v="([^"]+)"' -head 20
uvp F 'k="amenity"' -uniq 'v="([^"]+)"' -sort count -head 20   # by count
uvp F 'k="amenity"' -uniq 'v="([^"]+)"' -sort value -head 20   # by value
uvp F 'ERROR' -uniq 'code=([0-9]+)' --csv                      # --csv / --json

Measured (top 20, second run): 3 GB rg 0.44 s / uvp 1.02 s (rg wins by 2.32×); 10 GB rg 10.98 / uvp 1.94 (5.66×); 50 GB rg 55.36 / uvp 7.02 (7.89×). By value (-sort value) at 50 GB: rg 62.59 / uvp 6.86 (9.12×).

4-4. Order (sequence)

uvp F -seq 'login,timeout,restart'      # only flows where they appear in that order

grep can only ask about a set of words, so there is no CLI equivalent (you would write a state machine in awk). 3 GB 1.18 s, 10 GB 2.85 s, 50 GB 13.20 s (second run).

4-5. First and last

uvp F 'Tokyo' -head 10      # first 10
uvp F 'Tokyo' -tail 10      # last 10

A -head N written right after a search reads from the start of the file and stops as soon as it has N hits (since v1.6.2).
The output is byte-for-byte what rg … | head -N prints, and it is the same on every run.

東京, second run 3 GB 10 GB 50 GB
rg \| head -10-head 10 rg 0.06 s / uvp 0.44 s 0.08 / 0.45 0.05 / 0.55 (rg wins; both sub-second)
rg \| tail -10-tail 10 rg 0.34 / uvp 0.97 10.96 / 1.63 (6.72×) 56.04 / 6.37 (8.80×)

head goes to rg, which can stop after N hits (uvp carries its start-up and index-load cost; the gap is 0.4–0.5 s). tail has to reach the end of the file, so uvp wins; sort | head (the shape you actually write in practice) makes sort demand every line, so it lands on the same side.

A -head N written after -uniq / -sort / -seq still means “the first N rows of the transformed list” (the top 20 of a ranking, say).

Up to v1.6.1, -head searched the whole file and then cut the list, which took 6.7–7.0 s on 50 GB.


5. Options

-limit N — how many hits to keep (a guard for memory)

uvp F 'ERROR' -limit 1000      # stop once 1,000 hits have piled up
uvp F 'ERROR' -limit none      # unlimited (0 works too; this is the default)

The default is unlimited (since v1.6.2; it used to be 1,000,000). The GUI and the CLI share the setting; -limit overrides it for one run.

Hitting the limit returns exit code 2 plus a warning. The count is shared across all shards, and
which hits you keep is not defined — whichever shards filled the budget first.

For the first N hits, use -head N, not -limit (previous section; it matches rg | head -N).
-limit is the seatbelt that keeps a huge result from eating your memory.

50 GB, 東京 (94,979 hits), second run Time Note
-head 10 0.55 s stops after the first 10 from the start of the file (rg \| head -10: 0.05 s)
-limit 1000 0.68 s stops once 1,000 piled up (which 1,000 is undefined; exit 2. rg \| head -1000: 0.09 s)
default (unlimited, all hits) 7.41 s rg 56.72 s (7.65×)

-out <file> — where output goes

uvp F 'ERROR' -out hits.txt          # text
uvp F 'ERROR' -out hits.txt.gz       # .gz / .zip are compressed as they are written
uvp F 'ERROR' -out hits.txt --force  # overwrite (refused by default)

Measured (second run): text, rg -n > file 50 GB 55.36 s / uvp 5.92 s (9.35×), 10 GB 22.23 / 1.80 (12.35×). Compressed, rg \| gzip > out.gz 50 GB 71.91 s / uvp -out out.txt.gz 6.17 s (11.65×).

The rest

Option Meaning CLI counterpart
--no-line-number omit line numbers matches sed -n '/term/p' output
--csv / --json tally output format
-open at the front: run in the GUI / at the end: run, then also open the GUI

Measured for --no-line-number (against sed, second run): 3 GB sed 9.91 s / uvp 1.05 s (9.44×); 10 GB sed 13.11 / uvp 1.61 (8.14×); 50 GB sed 145.35 / uvp 6.01 (24.18×). sed works line by line, so even 3 GB takes 10 s.


6. From the CLI into the GUI — -open (the point of uvp)

uvp lets you carry an investigation from the terminal into the window. Same engine, same .uwvz, so there is nothing to wait for on arrival.

# -open at the end … run in the CLI, print the result, and hand it to the GUI as well
uvp osm/japan-latest.osm 'k="amenity"' -uniq 'v="([^"]+)"' -head 20 -limit none -open

# -open at the front … don't run in the CLI; run it in the GUI and watch it there
uvp -open osm/japan-latest.osm 'k="amenity"' -uniq 'v="([^"]+)"' -head 20 -limit none

With -open at the end — the terminal prints the result, and the same tally appears in the app.

A tally command run with -open against a 51 GB OSM file. The terminal at the bottom shows the results, and the same tally is listed in the GUI's Drill-down / Tally window

With -open at the front — the GUI does the work, and you carry on with the mouse.

uvp -open launched the app and shows the tally for k=amenity — 1,000,000 matches, 391 distinct values, 4.45 seconds. Clicking a row narrows to the next stage

The part that matters — it stays fast after you arrive

In the window you can keep adding questions to the same file.

  • Click a row and it becomes the next stage (“Click a row to narrow down to that value in a new stage.”). Click parking and a new tab opens for k="amenity" and parking
  • Stages after the first only look inside the previous stage, so there is no wait. Changed your mind? Go back to that tab. You never rebuild a pipeline and re-read 51 GB
  • Switch between Drill-down / Sequence, and hit Tally, Sort or Save CSV right there
  • On a 51 GB, 892-million-line file, the tally on screen took 4.45 s (see “Last: Tally 4.45 s” in the status bar)

So the way to use uvp is:

CLI in scripts and cron (several times faster than rg past 10 GB). Once you have a lead, -open hands it to the GUI, where you can re-narrow as many times as you like.

Faster than rg on its own, and once it is on screen you can change the question with the mouse — that round trip is what a grep / rg pipeline cannot give you.


7. Replacing text — -replace (Edit license)

# CLI
sed 's/Tokyo/TOKYO/g' F > out.txt
sed -E 's/v="(bus_stop|traffic_signals)"/v="X_\1"/g' F > out.txt
rg -N -F --passthru 'Tokyo' -r TOKYO F > out.txt

# uvp (the source file is never modified; the whole text is written out)
uvp F 'Tokyo' -replace TOKYO -out out.txt
uvp F 'v="(bus_stop|traffic_signals)"' -E -replace 'v="X_$1"' -out out.txt

With -E you can insert capture groups as $1, $2 ($$ is a literal $). The output is byte-for-byte identical to sed / rg --passthru — verified on every size.

Measured (time to write the whole file out; uvp 1.6.2, 16 September 2026 — the replace path did not change in v1.6.3, so it was not re-measured)

3 GB 10 GB 50 GB
uvp -replace 4.57 s 13.47 s 69.75 s
sed 's/A/B/g' 14.94 s (3.27×) 26.51 s (1.97×) 187.74 s (2.69×)
rg --passthru -r 7.06 s (1.54×) 26.89 s (2.00×) 146.65 s (2.10×)
uvp -E -replace '$1' 4.17 s 14.08 s 69.95 s
sed -E (backreferences) 24.81 s (5.95×) 78.36 s (5.57×) 405.52 s (5.80×)

A replacement with backreferences runs about 6× faster than sed — 6 min 45 s versus 1 min 10 s at 50 GB.

Note that a replacement counts occurrences, not lines: for Tokyo in the 3 GB file, 11,274 lines matched but 11,361 occurrences were replaced — 87 lines held more than one.

-replace is part of the Edit license (included in the 14-day free trial). It never rewrites the file; it writes the whole text out.


8. .uwvz — build it once, search it even without the original

uvp convert huge.log              # build the .uwvz (3 GB 1.2 s, 10 GB 13.7 s, 50 GB 66 s)
uvp huge.log.uwvz 'ERROR'         # search it with the original deleted
uvp huge.log.uwvz -extract -out restored.log   # restore the original (free)
  • It is 1/9 to 1/12 of the original (3 GB → 0.25 GB, 50 GB → 5.74 GB) — about the same as gzip, but 5× faster to create
  • Unlike gzip, you search it without unpacking (5.81 s at 50 GB, second run — 12.6× the 73.32 s rg takes to scan the original). You can also point it at a .gz directly (uvp f.gz 'TERM'): it builds the .uwvz on the first run and reuses it after that (50 GB: first run 120.26 s, second 6.88 s)
  • -extract needs no license: 3.55 s for 3 GB, byte-identical to the original (measured with v1.6.2)

More in Keep it as .uwvz instead of gzip.


9. Calling it from a script

#!/usr/bin/env bash
if uvp /var/log/app.log 'FATAL' -limit 100 -out /tmp/fatal.txt; then
  mail -s "FATAL found" ops@example.com < /tmp/fatal.txt
fi
Exit code Meaning
0 found
1 not found (same as grep)
2 error, or the limit was hit and output was cut off
  • Progress and diagnostics go to stderr, data to stdout — safe to pipe
  • For large results, write to a file with -out or cap them with -limit (memory grows with the hit count)

10. Where uvp is the wrong tool (honestly)

  • Around 3 GB, when the file is already in the OS cache. On the second run rg wins the plain search by 3.15×, -i by 1.98×, -E by 2.51× and two-term narrowing by 2.26× (the gap is 0.6–0.7 s). Cold, though — right after a reboot, a log you last opened days ago, a USB HDD — uvp is 2.2–2.9× faster on the same 3 GB, and on the two-run total (cold + hot) it wins -i, -E, narrowing and tallies by 1.3–1.7×
  • When you ask exactly one question. There is no point building an index: with no .uwvz yet, rg wins the first question at every size by 1.2–1.4× (the difference is the index build)
  • When you only want the first N hits. rg | head can stop after N, so rg wins at every size (both sub-second)
  • In the middle of a pipeline. uvp is built around a .uwvz you ask many questions of. A one-shot that feeds another command is what the CLI is good at

Where it pays off is the second question against the same file. From 10 GB up it is 4.3–6.3× even hot, and 2–24× at 50 GB (-head alone goes to rg).


11. How this was measured, and how to reproduce it

  • Hardware: Mac M4, 32 GB RAM, external USB SSD (an internal SSD makes everything faster)
  • Data: OpenStreetMap Japan (XML), 3,032,812,644 / 10,255,315,281 / 51,254,526,392 bytes
  • Compared against: ripgrep 15.2.0, BSD sed, sort, uniq (rg run with --no-config --color never). uvf / uvp 1.6.3, 17 September 2026
  • Each item: syncsudo purge → 10 s wait → cold → hot immediately after. This page shows the hot (second-run, .uwvz reused) figures, with rg on the same hot run. Cold and two-run totals are on the benchmarks page
  • Output was diffed against rg / sed on every check — 78 of 78 matched, zero mismatches (replace and -extract are the 16 September v1.6.2 measurements)

Numbers move with the environment. If yours come out different, please tell us — we will check and correct this page.


Copied title and URL