curl/trurl: a command line tool for URL parsing and manipulation
GitHub describes it as a command line tool for URL parsing and manipulation.. The repository metadata lists Perl as its primary language. The metadata lists the NOASSERTION license. This article stays within the project description and details documented in the GitHub repository README.
At a glance
- What is it?
- trurl parses, rewrites and normalizes URLs from the shell, built on libcurl's URL API. It is a small C program with a real dependency floor, and it is the wrong tool for fetching anything.
- Who is it for?
- Adopt trurl if you already link against libcurl and need a scriptable way to rewrite or normalize URLs without pulling in a language runtime. Skip it if you need to fetch the URL, or if you cannot guarantee libcurl 7.88.0 or newer, since punycode handling is gated on that version.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 29 days ago.
- What is it written in?
- Mainly Perl, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What trurl does that a shell one-liner cannot
The README describes trurl as a "Command line tool for URL parsing and manipulation". That is narrower than it sounds, and the narrowness is the point. It does not transfer data. It takes a URL apart into components, lets you change individual components, and prints the result.
The problem it solves is that string surgery on URLs is unreliable. Splitting on slashes breaks on userinfo, on IPv6 literals, on ports, on query strings that contain slashes. trurl delegates the parsing to libcurl's URL API, so the component boundaries follow the same rules curl itself uses when it makes a request. If your build script rewrites a host with sed and your application fetches with curl, you have two parsers disagreeing about what the URL means. trurl removes the second parser.
The audience is people writing build scripts, CI steps, log processors, and migration tooling. Anyone who has a list of URLs and needs to change one part of each. The README's own examples are all one-liners, which is a fair signal of the intended scale.
How trurl splits and reassembles a URL
The mechanism is a sequence of operations applied to a URL object. You supply the URL with --url, then apply modifiers in the order you write them, and trurl prints the result. Setting a component is --set, reading one is --get, and appending to a component is --append.
The README shows the component model directly. Extracting the port from https://curl.se/we/are.html with --get '{port}' prints 443, which means trurl fills in the scheme default rather than leaving the component empty. Extracting the path prints /we/are.html. Setting the host on https://curl.se to example.com prints https://example.com/, with the trailing slash added. So the output is a normalized URL, not a string edit.
Operations compose. The README's redirect example takes https://curl.se/we/are.html and applies --redirect here.html, producing https://curl.se/we/here.html. The port example resolves a dot segment first: https://curl.se/we/../are.html with --set port=8080 becomes https://curl.se:8080/are.html. Path normalization happens as part of the parse, before your modification is applied.
Output is not limited to a URL string. The --json flag emits an array with a url field and a parts object containing scheme, user, host, path and fragment. In the README's example the user component is ::moo:: in parts but percent-encoded as %3a%3amoo%3a%3a inside the url string. That distinction matters if you are piping into jq: the parts object holds decoded values, the url string holds the wire form.
Building trurl from source and running a first rewrite
The README gives the Linux path as a plain make. The Makefile pulls compiler and linker flags from curl-config unless TRURL_IGNORE_CURL_CONFIG is defined, so a working libcurl development install is what makes the build succeed.
make
cc -W -Wall -pedantic -g -c -o trurl.o trurl.c
cc trurl.o -lcurl -o trurlThose two lines are the README's own transcript of the compile and link steps. If curl-config is missing or libcurl headers are absent, the build stops before producing the trurl binary. The prerequisites section names libcurl 7.62.0 as the floor, with 7.80.0 and 7.81.0 needed for curl_url_strerror() and CURLUPART_ZONEID respectively.
Once the binary exists, the smallest useful operation is reading one component. This prints the port for a URL that does not state one explicitly:
trurl --url https://curl.se/we/are.html --get '{port}'The README says the output is 443. That is the scheme default being filled in, and it is the behaviour you want if you are grouping URLs by effective port rather than by what the author typed.
A more realistic first task is stripping campaign parameters from a list. The README's tracking example combines a literal URL with a glob pattern:
trurl "https://curl.se?search=hey&utm_source=tracker" --qtrim "utm_*"It prints https://curl.se/?search=hey. The glob is matched against query key names, so utm_source is removed and search survives. To apply the same trim to a file of URLs, the README documents reading from stdin with --url-file -, which means cat urllist.txt | trurl --url-file - followed by your modifiers.
If a query uses a semicolon instead of an ampersand, --query-separator ";" changes the split character, and the README pairs it with --qtrim in one example. Spaces in a path are rejected by default; --accept-space converts them, turning https://curl.se/this has space/index.html into https://curl.se/this%20has%20space/index.html.
The libcurl version floor is the real constraint
trurl is a thin front end. Almost every capability it exposes is a libcurl feature with a minimum version, and the README prints that mapping as a table. normalize-ipv needs 7.77.0. white-space needs 7.78.0. url-strerror needs 7.80.0. zone-id needs 7.81.0. punycode needs 7.88.0. punycode2idn needs 8.3.0. no-guess-scheme needs 8.9.0.
The README is explicit that trurl builds against libcurl older than 7.81.0 but "will then not work as well", and points to URL-QUIRKS.md for what happens when a feature is missing. That file is in the repository root, which means the degraded behaviour is documented rather than silent. Still, the failure mode is quiet: you get a build, you get a binary, and some flags behave differently than the manual describes. On a long-lived distribution you may be pinned to a libcurl older than several of those rows.
The documented escape hatch is trurl --version, which the README says reports the features your build supports and the libcurl version it was built with. That is the check to run before you write a script that depends on punycode conversion or zone-id handling. Do not infer capability from the trurl release number alone; the same trurl source built against two libcurl versions is two different tools.
The second constraint is scope. trurl never makes a network request. It cannot tell you whether a URL resolves, redirects, or returns 200. If your task is checking that a rewritten URL still works, trurl gets you the string and something else has to fetch it.
trurl compared with doing the same work in curl or a scripting language
The obvious alternative is curl itself. curl fetches; trurl manipulates. They overlap only in that both use libcurl's URL parser, so a URL you normalize with trurl is interpreted the same way when curl requests it. If you need the response, curl is the tool and trurl is not a substitute. The README frames trurl as a companion, and the shared parser is the reason to keep them together.
A second alternative is a general-purpose language. Python's urllib.parse, for instance, will split a URL into components and reassemble it, and it is already installed on most systems. The difference in approach is where the parsing rules come from. trurl inherits libcurl's rules, including the quirks documented in URL-QUIRKS.md and the version-gated behaviours above. A language library inherits its own rules, which may differ on edge cases like IPv6 zone identifiers or how a missing scheme is guessed. If the URLs you rewrite are later fetched by curl, matching curl's parser is the whole reason to prefer trurl. If they are fetched by something else, that argument disappears and the language you already have is the cheaper choice.
A third consideration is the build. Python needs no compile step. trurl needs a C compiler and libcurl development files, and on Windows the README routes you through Cygwin, where you must select curl, libcurl-devel, libcurl4, make and gcc-core during installation. That is a heavier setup than a language runtime you already have, and it is the cost of getting curl's parser exactly.
Maintenance, licensing and upgrade cost
The repository is not archived. The last push was on 2025-05-12, which is also the date of the trurl-0.16.1 release. The release before it, trurl 0.16, is dated 2024-09-19, and 0.15.1 is dated 2024-09-12. The spacing between 0.15.1 and 0.16 is a week; the gap to 0.16.1 is roughly eight months. That is a reasonable cadence for a small utility, but it is not a project that ships weekly, and the 0.x version numbers mean the command line surface is not frozen by a stability promise.
Upgrade cost is dominated by the libcurl floor rather than by trurl's own code. Moving to a trurl release that uses a newer libcurl feature raises the minimum version you must build against. The README's compatibility table is the document to diff when you upgrade, because a new trurl on an old libcurl is a supported but degraded combination.
The repository carries COPYING, a LICENSES/ directory and REUSE.toml, and the source headers carry SPDX-License-Identifier: curl. The Makefile header states the software "is licensed as described in the file COPYING". The repository metadata reports the licence as NOASSERTION, so the automated classifier did not identify a standard SPDX licence, even though the file headers use the curl identifier. If you need a definite answer on redistribution terms, read COPYING and the LICENSES/ directory rather than trusting the metadata field. This is not legal advice.
Editorial conclusion
Adopt trurl if you already link against libcurl and need a scriptable way to rewrite or normalize URLs without pulling in a language runtime. Skip it if you need to fetch the URL, or if you cannot guarantee libcurl 7.88.0 or newer, since punycode handling is gated on that version. Before relying on it, run trurl --version to see which feature table row your build actually satisfies, and check URL-QUIRKS.md for the behaviour you are depending on.
Frequently asked questions
What is trurl?
trurl is a command line tool for URL parsing and manipulation, built on libcurl's URL API. It takes URLs apart into components, lets you change them with flags such as --set, --get and --append, and prints the result. It does not fetch anything.
How do I install trurl?
On Linux the README says to compile the C source with make, which produces the trurl binary and its manual page; the make step needs libcurl development files. trurl is also listed in some package managers via the project wiki, and on Windows the README describes building under Cygwin with curl, libcurl-devel, libcurl4, make and gcc-core.
How is trurl different from curl?
curl transfers data over the network; trurl only parses and rewrites the URL string. Both use libcurl's URL parser, so a URL normalized by trurl is interpreted the same way when curl requests it. If you need the response body, trurl is not the tool.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/curl-trurl)