Package: pwebarc-wrrarms
What?wrrarms(pwebarc-wrrarms) is a tool for displaying and manipulatingPrivate Web Archive (pwebarc)Web Request+Response (WRR) files produced bypWebArc browser extension.QuickstartInstallationInstall with:pip install pwebarc-wrrarmsand run aswrrarms --helpAlternatively, install it via Nixnix-env -i -f ./default.nixAlternatively, run without installing:python3 -m wrrarms --helpBuilding a hierarchy of latest versions of all URLsAssuming you keep your WRR dumps in~/pwebarc/rawyou can generate awget-like file hierarchy of symlinks under~/pwebarc/latestpointing to the latest version of each URL in~/pwebarc/rawwithwrrarms organize --action symlink-update --output hupq --to ~/pwebarc/latest --and "status|== 200C" ~/pwebarc/rawor, using a bit better format:wrrarms organize --action symlink-update --output hupnq --to ~/pwebarc/latest --and "status|== 200C" ~/pwebarc/rawPersonally, I prefer theflatformat as I dislike deep file hierarchies and it allows to see and filter new dumps more easily inrangerfile browser:wrrarms organize --action symlink-update --output flat --to ~/pwebarc/latest --and "status|== 200C" ~/pwebarc/rawIf you have a lot of WRR files all of the above commands could be rather slow, so if you want to keep your tree updated in real-time you should use a two-stage--stdin0pipeline shown in theexamples sectionbelow instead.Previewing WRR filesSeewrrarms-xdg-openscriptfor a way toxdg-open.wrrfiles.Seewrrarms-w3mscriptfor an example of how you can usewrrarmsandw3mto turn your WRR files containing HTML pages into readable plain-text.
There is alsowrrarms-pandocscriptthat does the same viapandoc.See thescripts sub-directoryfor more examples.TODORendering into static website mirrors a-lawget -k.Currently, the extension archives everything except WebSockets data butwrrarms+pandoconly work well for dumps of mostly plain text websites (which is the main use case I use this whole thing for: scrape a website and then mass-convert everything to PDFs via somepandocmagic, then index those withrecoll).Converter frommitmproxydumps, HAR, WARC, and PCAP files into WRR.Converter from WRR to WARC.Data deduplication.Non-dumb server with time+URL index and replay, i.e. a localWayback Machine.Full text indexing and search.UsagewrrarmsA tool to pretty-print, compute and print values from, search, organize (programmatically rename/move/symlink/hardlink files), (WIP: check, deduplicate, and edit) pWebArc WRR (WEBREQRES, Web REQuest+RESponse) archive files.Terminology: areqres(Reqreswhen a Python type) is an instance of a structure representing HTTP request+response pair with some additional metadata.options:--version: show program's version number and exit-h, --help: show this help message and exit--markdown: show help messages formatted in Markdownsubcommands:{pprint,get,run,find,organize,stream}pprint: pretty-print WRR filesget: print expressions computed from a WRR file to stdoutrun: spawn a process on generated temporary files produced from expressions computed on WRR filesfind: print paths of WRR files matching specified criteriaorganize: rename/hardlink/symlink WRR files based on their metadatastream: produce a stream structured lists containing expressions computed from specified WRR files to stdout, a generalizedwrrarms getwrrarms pprintPretty-print given WRR files to stdout.positional arguments:PATH: inputs, can be a mix of files and directories (which will be traversed recursively)options:-u, --unabridged: print all data in full--abridged: shorten long strings for brevity (useful when you want to visually scan through batch data dumps) (default)error handling:--errors {fail,skip,ignore}: when an error occurs:fail: report failure and stop the execution (default)skip: report failure but skip the reqres that produced it from the output and continueignore:skip, but don't report the failurefilters:--or EXPR: only work on reqres which match any of these expressions...--and EXPR: ... and all of these expressions, both can be specified multiple times, both use the same expression format aswrrarms get --expr, which seewrrarms getCompute output values by evaluating expressionsEXPRs on a given reqres stored atPATH, then print them to stdout (terminating each value as specified).positional arguments:PATH: input WRR file pathoptions:-e EXPR, --expr EXPR: an expression to compute; can be specified multiple times in which case computed outputs will be printed sequentially, see also "output" options below; (default:response.body|es); each EXPR describes a state-transformer (pipeline) which starts from valueNoneand evaluates a script built from the following:constants and functions:es: replaceNonevalue with an empty string""eb: replaceNonevalue with an empty byte stringb""false: replaceNonevalue withFalsetrue: replaceNonevalue withTrue0: replaceNonevalue with01: replaceNonevalue with1not: apply logicalnotto valuelen: applylento valuestr: cast value tostror failbytes: cast value tobytesor failbool: cast value toboolor failint: cast value tointor failfloat: cast value tofloator failunquote: percent-encoding-unquote valueunquote_plus: percent-encoding-unquote value and replace+symbols with spacessha256: computehex(sha256(value.encode("utf-8"))==: apply== arg,argis cast to the same type as the current value!=: apply!= arg, similarly<: apply< arg, similarly<=: apply<= arg, similarly>: apply> arg, similarly>=: apply>= arg, similarlyprefix: take firstargcharacterssuffix: take lastargcharactersabbrev: leave the current value as if if its length is less or equal thanargcharacters, otherwise take firstarg/2followed by lastarg/2charactersreplace: replace all occurences of the first argument in the current value with the second argument, casts arguments to the same type as the current valuereqres fields, these work the same way as constants above, i.e. they replace current value ofNonewith field's value, if reqres is missing the field in question, which could happen forresponse*fields, the result isNone:version: WEBREQRES format version; intsource:+-separated list of applications that produced this reqres; strprotocol: protocol (e.g."HTTP/1.0","HTTP/2.0"); strrequest.started_at: request start time in milliseconds since 1970-01-01 00:00; EpochMsecrequest.method: request HTTP method ("GET","POST", etc); strrequest.url: request URL, including the fragment/hash part; strrequest.headers: request headers; list[tuple[str, bytes]]request.complete: is request body complete?; boolrequest.body: request body; bytesresponse.started_at: response start time in milliseconds since 1970-01-01 00:00; EpochMsecresponse.code: HTTP response code (like200,404, etc); intresponse.reason: HTTP response reason (like"OK","Not Found", etc); usually empty for Chromium and filled for Firefox; strresponse.headers: response headers; list[tuple[str, bytes]]response.complete: is response body complete?; boolresponse.body: response body; Firefox gives raw bytes, Chromium gives UTF-8 encoded strings; bytes | strfinished_at: request completion time in milliseconds since 1970-01-01 00:00; EpochMsecderived attributes:fs_path: file system path for the WRR file containing this reqres; strqtime_ms: aliast forrequest.started_at; mnemonic: "reQuest TIME"; intqtime:qtime_msrounded down to seconds (UNIX epoch); intqtime_msq: three least significant digits ofqtime_ms; intqyear: year number ofgmtime(qtime)(UTC year number ofqtime); intqmonth: month number ofgmtime(qtime); intqday: day of the month ofgmtime(qtime); intqhour: hour ofgmtime(qtime)in 24h format; intqminute: minute ofgmtime(qtime); intqsecond: second ofgmtime(qtime); intstime_ms:response.started_atif there was a response,finished_atotherwise; mnemonic: "reSponse TIME"; intstime:stime_msrounded down to seconds (UNIX epoch); intstime_msq: three least significant digits ofstime_msq; intsyear: similar tosyear, but forstime; intsmonth: similar tosmonth, but forstime; intsday: similar tosday, but forstime; intshour: similar toshour, but forstime; intsminute: similar tosminute, but forstime; intssecond: similar tossecond, but forstime; intftime_ms: aliast forfinished_at; intftime:ftime_msrounded down to seconds (UNIX epoch); intftime_msq: three least significant digits offtime_msq; intfyear: similar tosyear, but forftime; intfmonth: similar tosmonth, but forftime; intfday: similar tosday, but forftime; intfhour: similar toshour, but forftime; intfminute: similar tosminute, but forftime; intfsecond: similar tossecond, but forftime; intstatus:"NR"if there was no response,str(response.code) + "C"if response was complete,str(response.code) + "N"otherwise; strmethod: aliast forrequest.method; strfull_url: aliast forrequest.url; strnet_url:request.urlwithout the fragment/hash part, if any, this is the URL that actually gets sent to the server; strscheme: scheme part ofrequest.url(http,https); strnetloc: netloc part ofrequest.url(i.e., in the most general case,<username>:<password>@<hostname>:<port>)hostname: hostname part ofrequest.urlrhostname: hostname part ofrequest.urlwith the order of parts reversed, e.g."https://www.example.com"->"com.example.www"raw_path: raw path part ofrequest.url, e.g."https://www.example.com"->"","https://www.example.com/"->"/","https://www.example.com/index.html"->"/index.html"path:raw_pathwithout the leading slash, if any, e.g."https://www.example.com"->"","https://www.example.com/"->"","https://www.example.com/index.html"->"index.html"ipath:path + "index.html"ifpathis empty or ends with a slash,pathotherwisequery: query part ofrequest.url(everything after the?character and before the#character)nquery: normalizedquery(with empty query parameters removed)nquery_url:full_urlwith normalizedquery; stroqm: optional query mark:?character ifqueryis non-empty, an empty string otherwise; strfragment: fragment (hash) part of the url; strofm: optional fragment mark:#character iffragmentis non-empty, an empty string otherwise; stra compound expression built by piping (|) the above, for example:net_url|sha256net_url|sha256|prefix 4path|unquotequery|unquote_plus|abbrev 128response.complete: this will print the value ofresponse.completeorNone, if there was no responseresponse.complete|false: this will printresponse.completeorFalseresponse.body|eb: this will printresponse.bodyor an empty string, if there was no responseoutput:--not-terminated: don't terminate output values with anything, just concatenate them (default)-l, --lf-terminated: terminate output values with\n(LF) newline characters-z, --zero-terminated: terminate output values with\0(NUL) byteswrrarms runCompute output values by evaluating expressionsEXPRs for each ofNUMreqres stored atPATHs, dump the results into into newly generated temporary files (terminating each value as specified), spawn a givenCOMMANDwith given argumentsARGs and the resulting temporary file paths appended as the lastNUMarguments, wait for it to finish, delete the temporary files, exit with the return code of the spawned process.positional arguments:COMMAND: command to spawnARG: additional arguments to give to the COMMANDPATH: input WRR file paths to be mapped into new temporary filesoptions:-e EXPR, --expr EXPR: the expression to compute, can be specified multiple times, see{__package__} get --exprfor more info; (default:response.body|es)-n NUM, --num-args NUM: number ofPATHs (default:1)output:--not-terminated: don't terminate output values with anything, just concatenate them (default)-l, --lf-terminated: terminate output values with\n(LF) newline characters-z, --zero-terminated: terminate output values with\0(NUL) byteswrrarms findPrint paths of WRR files matching specified criteria.positional arguments:PATH: inputs, can be a mix of files and directories (which will be traversed recursively)options:--stdin0: read zero-terminatedPATHs from stdin, these will be processed afterPATHs specified as command-line arguments, requires specified--toerror handling:--errors {fail,skip,ignore}: when an error occurs:fail: report failure and stop the execution (default)skip: report failure but skip the reqres that produced it from the output and continueignore:skip, but don't report the failurefilters:--or EXPR: only work on reqres which match any of these expressions...--and EXPR: ... and all of these expressions, both can be specified multiple times, both use the same expression format aswrrarms get --expr, which seeoutput:-l, --lf-terminated: output absolute paths of matching WRR files terminated with\n(LF) newline characters to stdout (default)-z, --zero-terminated: output absolute paths of matching WRR files terminated with\0(NUL) bytes to stdoutwrrarms organizeRename/hardlink/symlink given WRR files toDESTINATIONbased on their metadata.Operations that could lead to accidental data loss are not permitted.
E.g.wrrarms organize --action renamewill not overwrite any files, which is why the default--outputcontains%(num)d.positional arguments:PATH: inputs, can be a mix of files and directories (which will be traversed recursively)options:--dry-run: perform a trial run without actually performing any changes-q, --quiet: don't log computed updates to stderr-a {rename,hardlink,symlink,symlink-update}, --action {rename,hardlink,symlink,symlink-update}: organize how:rename: rename source files underDESTINATION, will fail if target already exists (default)hardlink: create hardlinks from source files to paths underDESTINATION, will fail if target already existssymlink: create symlinks from source files to paths underDESTINATION, will fail if target already existssymlink-update: create symlinks from source files to paths underDESTINATION, will overwrite the target ifstime_msfor the source reqres is newer than the same value for the target--batch-number INT: batch at most this many--actions together (default:1024), making this larger improves performance at the cost of increased memory consumption, setting it to zero will force all--actions to be applied immediately--lazy: sets--batch-numberto positive infinity; most useful in combination with--action symlink-updatein which case it will forcewrrarmsto compute the desired file system state first and then perform disk writes in a single batch-o FORMAT, --output FORMAT: format describing the generated output path, an alias name or a custom pythonic %-substitution string:available aliases and corresponding %-substitutions:default:%(syear)d/%(smonth)02d/%(sday)02d/%(shour)02d%(sminute)02d%(ssecond)02d%(stime_msq)03d_%(qtime_ms)s_%(method)s_%(net_url|sha256|prefix 4)s_%(status)s_%(hostname)s.%(num)d.wrr(default)short:%(syear)d/%(smonth)02d/%(sday)02d/%(stime_ms)d_%(qtime_ms)s.%(num)d.wrrsurl:%(scheme)s/%(netloc)s/%(path)s%(oqm)s%(query)surl:%(netloc)s/%(path)s%(oqm)s%(query)ssurl_msn:%(scheme)s/%(netloc)s/%(path)s%(oqm)s%(query)s_%(method)s_%(status)s.%(num)d.wrrurl_msn:%(netloc)s/%(path)s%(oqm)s%(query)s_%(method)s_%(status)s.%(num)d.wrrshpq:%(scheme)s/%(hostname)s/%(ipath|abbrev 120)s%(oqm)s%(query|abbrev 120)s.wrrhpq:%(hostname)s/%(ipath|abbrev 120)s%(oqm)s%(query|abbrev 120)s.wrrshpq_msn:%(scheme)s/%(hostname)s/%(ipath|abbrev 120)s%(oqm)s%(query|abbrev 100)s_%(method)s_%(status)s.%(num)d.wrrhpq_msn:%(hostname)s/%(ipath|abbrev 120)s%(oqm)s%(query|abbrev 100)s_%(method)s_%(status)s.%(num)d.wrrshupq:%(scheme)s/%(hostname)s/%(ipath|unquote|abbrev 120)s%(oqm)s%(query|unquote_plus|abbrev 120)s.wrrhupq:%(hostname)s/%(ipath|unquote|abbrev 120)s%(oqm)s%(query|unquote_plus|abbrev 120)s.wrrshupq_msn:%(scheme)s/%(hostname)s/%(ipath|unquote|abbrev 120)s%(oqm)s%(query|unquote_plus|abbrev 100)s_%(method)s_%(status)s.%(num)d.wrrhupq_msn:%(hostname)s/%(ipath|unquote|abbrev 120)s%(oqm)s%(query|unquote_plus|abbrev 100)s_%(method)s_%(status)s.%(num)d.wrrsrhupq:%(scheme)s/%(rhostname)s/%(ipath|unquote|abbrev 120)s%(oqm)s%(query|unquote_plus|abbrev 100)s.wrrrhupq:%(rhostname)s/%(ipath|unquote|abbrev 120)s%(oqm)s%(query|unquote_plus|abbrev 100)s.wrrsrhupq_msn:%(scheme)s/%(rhostname)s/%(ipath|unquote|abbrev 120)s%(oqm)s%(query|unquote_plus|abbrev 100)s_%(method)s_%(status)s.%(num)d.wrrrhupq_msn:%(rhostname)s/%(ipath|unquote|abbrev 120)s%(oqm)s%(query|unquote_plus|abbrev 100)s_%(method)s_%(status)s.%(num)d.wrrshupnq:%(scheme)s/%(hostname)s/%(ipath|unquote|abbrev 120)s%(oqm)s%(nquery|unquote_plus|abbrev 120)s.wrrhupnq:%(hostname)s/%(ipath|unquote|abbrev 120)s%(oqm)s%(nquery|unquote_plus|abbrev 120)s.wrrshupnq_msn:%(scheme)s/%(hostname)s/%(ipath|unquote|abbrev 120)s%(oqm)s%(nquery|unquote_plus|abbrev 120)s_%(method)s_%(status)s.%(num)d.wrrhupnq_msn:%(hostname)s/%(ipath|unquote|abbrev 120)s%(oqm)s%(nquery|unquote_plus|abbrev 120)s_%(method)s_%(status)s.%(num)d.wrrflat:%(hostname)s/%(ipath|unquote|replace / __|abbrev 120)s%(oqm)s%(nquery|unquote_plus|replace / __|abbrev 100)s_%(method)s_%(net_url|sha256|prefix 4)s_%(status)s.wrravailable substitutions:num: number of times an output path like this was seen; this value gets incremened for each new WRR file generating the same path withnumset to0and when the file at the path generated with the current value ofnumalready exists; i.e. adding this parameter to your--outputformat will ensure all generated file names will be uniqueall expressions ofwrrarms get --expr, which see-t DESTINATION, --to DESTINATION: target directory, when unset each sourcePATHmust be a directory which will be treated as its ownDESTINATION--stdin0: read zero-terminatedPATHs from stdin, these will be processed afterPATHs specified as command-line arguments, requires specified--toerror handling:--errors {fail,skip,ignore}: when an error occurs:fail: report failure and stop the execution (default)skip: report failure but skip the reqres that produced it from the output and continueignore:skip, but don't report the failurefilters:--or EXPR: only work on reqres which match any of these expressions...--and EXPR: ... and all of these expressions, both can be specified multiple times, both use the same expression format aswrrarms get --expr, which seeoutput:--no-output: don't print anything to stdout (default)-l, --lf-terminated: output absolute paths of newly produced files terminated with\n(LF) newline characters to stdout-z, --zero-terminated: output absolute paths of newly produced files terminated with\0(NUL) bytes to stdoutwrrarms streamCompute given expressions for each of given WRR files, encode them into a requested format, and print the result to stdout.positional arguments:PATH: inputs, can be a mix of files and directories (which will be traversed recursively)options:-u, --unabridged: print all data in full--abridged: shorten long strings for brevity (useful when you want to visually scan through batch data dumps) (default)--format {py,cbor,json,raw}: generate output in:py: Pythonic Object Representation akarepr(default)cbor: CBOR (RFC8949)json: JavaScript Object Notation aka JSON (binary data can't be represented, UNICODE replacement characters will be used)raw: concatenate raw values (termination is controlled by*-terminatedoptions)-e EXPR, --expr EXPR: an expression to compute, seewrrarms get --exprfor more info on expression format, can be specified multiple times (default:[]); to dump all the fields of a reqres, specify "."error handling:--errors {fail,skip,ignore}: when an error occurs:fail: report failure and stop the execution (default)skip: report failure but skip the reqres that produced it from the output and continueignore:skip, but don't report the failurefilters:--or EXPR: only work on reqres which match any of these expressions...--and EXPR: ... and all of these expressions, both can be specified multiple times, both use the same expression format aswrrarms get --expr, which see--format=rawoutput:--not-terminated: don't terminaterawoutput values with anything, just concatenate them-l, --lf-terminated: terminaterawoutput values with\n(LF) newline characters (default)-z, --zero-terminated: terminaterawoutput values with\0(NUL) bytesExamplesPretty-print all reqres in../dumb_server/pwebarc-dumpusing an abridged (for ease of reading and rendering) verbose textual representation:wrrarms pprint ../dumb_server/pwebarc-dumpPipe response body from a given WRR file to stdout:wrrarms get ../dumb_server/pwebarc-dump/path/to/file.wrrGet first 4 characters of a hex digest of sha256 hash computed on the URL without the fragment/hash part:wrrarms get -e "net_url|sha256|prefix 4" ../dumb_server/pwebarc-dump/path/to/file.wrrPipe response body from a given WRR file to stdout, but less efficiently, by generating a temporary file and giving it tocat:wrrarms run cat ../dumb_server/pwebarc-dump/path/to/file.wrrThuswrrarms runcan be used to do almost anything you want, e.g.wrrarms run less ../dumb_server/pwebarc-dump/path/to/file.wrrwrrarms run -- sort -R ../dumb_server/pwebarc-dump/path/to/file.wrrwrrarms run -n 2 -- diff -u ../dumb_server/pwebarc-dump/path/to/file-v1.wrr ../dumb_server/pwebarc-dump/path/to/file-v2.wrrList paths of all WRR files from../dumb_server/pwebarc-dumpthat contain only complete200 OKresponses with bodies larger than 1K:wrrarms find --and "status|== 200C" --and "response.body|len|> 1024" ../dumb_server/pwebarc-dumpRename all WRR files in../dumb_server/pwebarc-dump/defaultaccording to their metadata using--output default(see thewrrarms organizesection for its definition, thedefaultformat is designed to be human-readable while causing almost no collisions, thus makingnumsubstitution parameter to almost always stay equal to0, making things nice and deterministic):wrrarms organize ../dumb_server/pwebarc-dump/defaultalternatively, just show what would be donewrrarms organize --dry-run ../dumb_server/pwebarc-dump/defaultThe output ofwrrarms organize --zero-terminatedcan be piped intowrrarms organize --stdin0to perform complex updates. E.g. the following will rename new reqres from../dumb_server/pwebarc-dumpto~/pwebarc/rawrenaming them with--output default, theforloop is there to preserve profiles:for arg in ../dumb_server/pwebarc-dump/* ; do
  wrrarms organize --zero-terminated --to ~/pwebarc/raw/"$(basename "$arg")" "$arg"
done > changesthen, we can reusechangesto symlink all new files from~/pwebarc/rawto~/pwebarc/allusing--output hupq_msn, which would show most of the URL in the file name:wrrarms organize --stdin0 --action symlink --to ~/pwebarc/all --output hupq_msn < changesand then, we can reusechangesagain and use them to update~/pwebarc/latest, filling it with symlinks pointing to the latest200 OKcomplete reqres from~/pwebarc/raw, similar to whatwget -rwould produce (exceptwgetwould do network requests and produce responce bodies, while this will build a file system tree of symlinks to WRR files in/pwebarc/raw):wrrarms organize --stdin0 --action symlink-update --to ~/pwebarc/latest --output hupq --and "status|== 200C" < changesAdvanced examplesPretty-print all reqres in../dumb_server/pwebarc-dumpby dumping their whole structure into an abridged Pythonic Object Representation (repr):wrrarms stream --expr . ../dumb_server/pwebarc-dumpwrrarms stream -e . ../dumb_server/pwebarc-dumpPretty-print all reqres in../dumb_server/pwebarc-dumpusing the unabridged verbose textual representation:wrrarms pprint --unabridged ../dumb_server/pwebarc-dumpwrrarms pprint -u ../dumb_server/pwebarc-dumpPretty-print all reqres in../dumb_server/pwebarc-dumpby dumping their whole structure into the unabridged Pythonic Object Representation (repr) format:wrrarms stream --unabridged --expr . ../dumb_server/pwebarc-dumpwrrarms stream -ue . ../dumb_server/pwebarc-dumpProduce a JSON list of[<file path>, <time it finished loading in milliseconds since UNIX epoch>, <URL>]tuples (one per reqres) and pipe it intojqfor indented and colored output:wrrarms stream --format=json -ue fs_path -e finished_at -e request.url ../dumb_server/pwebarc-dump | jq .Similarly, but produce a CBOR output:wrrarms stream --format=cbor -ue fs_path -e finished_at -e request.url ../dumb_server/pwebarc-dump | lessConcatenate all response bodies of all the requests in../dumb_server/pwebarc-dump:wrrarms stream --format=raw --not-terminated -ue "response.body|es" ../dumb_server/pwebarc-dump | lessPrint all unique visited URLs, one per line:wrrarms stream --format=raw --lf-terminated -ue request.url ../dumb_server/pwebarc-dump | sort | uniqSame idea, but using NUL bytes while processing, and prints two URLs per line:wrrarms stream --format=raw --zero-terminated -ue request.url ../dumb_server/pwebarc-dump | sort -z | uniq -z | xargs -0 -n2 echoHandling binary dataTrying to use response bodies produced bywrrarms stream --format=jsonis likely to result garbled data as JSON can't represent raw sequences of bytes, thus binary data will have to be encoded into UNICODE using replacement characters:wrrarms stream --format=json -ue . ../dumb_server/pwebarc-dump/path/to/file.wrr | jq .The most generic solution to this is to use--format=cborinstead, which would produce a verbose CBOR representation equivalent to the one used by--format=jsonbut with binary data preserved as-is:wrrarms stream --format=cbor -ue . ../dumb_server/pwebarc-dump/path/to/file.wrr | lessOr you could just dump raw response bodies separately:wrrarms stream --format=raw -ue response.body ../dumb_server/pwebarc-dump/path/to/file.wrr | lesswrrarms get ../dumb_server/pwebarc-dump/path/to/file.wrr | less