Operations that pull structured information out of text or markup. Some of these operations belong to another category too, where their detailed description, options and examples live: Extract dates is documented under Date / Time, Extract EXIF under Multimedia, and Regular expression under Utils. Operations documented in full below are grouped here.
Operations are listed alphabetically.
| Operation | Subcommand | Reference |
|---|---|---|
| CSS selector | css-selector |
CSS selectors |
| Extract Audio Metadata | extract-audio-metadata |
Audio file format |
| Extract EXIF | extract-exif |
Multimedia |
| Extract Files | extract-files |
File carving |
| Extract ID3 | extract-id3 |
ID3 |
| Extract IP addresses | extract-ip-addresses |
IP address |
| Extract MAC addresses | extract-mac-addresses |
MAC address |
| Extract URLs | extract-urls |
URL |
| Extract dates | extract-dates |
Date / Time |
| Extract domains | extract-domains |
Domain name |
| Extract email addresses | extract-email-addresses |
Email address |
| Extract file paths | extract-file-paths |
Path (computing) |
| Extract hashes | extract-hashes |
Cryptographic hash functions |
| JPath expression | jpath-expression |
JSONPath |
| Jsonata Query | jsonata-query |
JSONata |
| RAKE | rake |
Keyword extraction |
| Regular expression | regular-expression |
Utils |
| Strings | strings |
strings (Unix) |
| Template | template |
Handlebars |
| XPath expression | xpath-expression |
XPath |
Extracts elements from an HTML/XML document using a CSS selector, serializing
each matched node and joining the results with a delimiter. Parsing is lenient
(@xmldom/xmldom semantics) and selection
follows CSS3 (nwmatcher semantics).
The input is parsed as XML (as CyberChef does), so it has a single root
element — content after the root element closes is ignored — and the five XML
entities plus numeric character references are decoded. Matched nodes are
serialized the way xmldom does: empty elements self-close (<br/>), attribute
values are normalized to double quotes, and text/attribute special characters are
escaped.
| Option | Type | Default | Notes |
|---|---|---|---|
--css-selector |
string | (empty) | The selector. Supports type, *, .class, #id, [attr]/[attr=val] (with ~= | ^= $= *= |=), the > + ~ combinators, comma groups, and structural pseudo-classes (:first-child, :last-child, :nth-child(an+b), :first-of-type, :not(...), :empty, :root, …). An empty selector yields empty output. |
--delimiter |
string | \n |
Joins the serialized matches. Backslash escapes are interpreted (\n → newline, \t → tab). |
Element type and attribute names are matched case-insensitively (HTML semantics);
class names, ids and attribute values are case-sensitive, except the HTML
enumerated attributes (type, dir, lang, …) whose values are also
case-insensitive. As in CyberChef, the state/rendering-dependent pseudo-classes
(:checked, :disabled, :enabled, :hover, …) and the checked/selected
attribute selectors never match.
cchef css-selector -i "<ul><li>Home</li><li>About</li></ul>" --css-selector "li"Output:
<li>Home</li>
<li>About</li>
Select <a> elements that carry both the nav class and an href attribute,
joined with | (note the single XML root — the elements are wrapped in a
<div> so all three siblings are queryable):
cchef css-selector -i '<div><a href="/x" class="nav">1</a><a>2</a><a href="/y" class="nav">3</a></div>' --css-selector "a.nav[href]" --delimiter " | "Output:
<a href="/x" class="nav">1</a> | <a href="/y" class="nav">3</a>
Reads the metadata out of an audio file and reports it as one JSON document, whatever the container. Ten formats are recognized from their opening bytes — MP3, WAV (including BWF and BW64), FLAC, OGG, Opus, AAC, AC3, WMA, MP4/M4A and AIFF — and each is read with the systems that format carries.
The report always has the same shape, so the same fields can be read whatever the file was:
| Section | Holds |
|---|---|
artifact |
The filename given, the length in bytes, and the container detected. |
detections |
Which metadata systems were found, such as id3v2, vorbis_comments or asf_content_desc. |
tags.common |
Ten tags every format is boiled down to: title, artist, album, date, track, genre, comment, composer, copyright, language. |
tags.raw |
Everything as the format itself records it, under a key per system. |
embedded |
Payloads carried inside the file — cover art, encapsulated objects, XML chunks. |
provenance |
Content credentials (C2PA), when a carrier for them is present. |
errors |
Anything that could not be read. |
A tag in tags.common is filled in by the first system that names it, so a file
carrying both an ID3v2 and an ID3v1 tag takes the ID3v2 value and keeps it.
| Option | Type | Default | Notes |
|---|---|---|---|
--filename-optional |
string | (empty) | Recorded in the report. It plays no part in reading the file — the container is detected from the bytes. Surrounding spaces are trimmed, and an empty name is reported as null. |
--max-embedded-text-bytes |
number | 524288 |
How much of an embedded text payload is kept. Payloads longer than this are cut and marked truncated. Values below 1024 are raised to it. |
Input that is not audio is not an error: the container is reported as unknown
and the reason is recorded under errors.
cchef extract-audio-metadata --in-file galway.flac --filename-optional "galway.flac" | cchef json-beautifyOutput (abridged):
{
"schema_version": "audio-meta-1.0",
"artifact": {
"filename": "galway.flac",
"byte_length": 132,
"container": {
"type": "flac",
"brand": null,
"mime": "audio/flac"
}
},
"detections": {
"metadata_systems": [
"flac_metablocks",
"vorbis_comments"
],
"provenance_systems": []
},
"tags": {
"common": {
"title": "Galway",
"artist": "Kevin MacLeod",
...
}
}
}
The common tags are the quickest way to read a file, whatever it is. Pull them out with JPath expression:
cchef bake --in-file galway.flac -r recipe.jsonwhere recipe.json chains the two operations:
[
{ "op": "Extract Audio Metadata", "args": ["galway.flac", 524288] },
{ "op": "JPath expression", "args": ["$.tags.common", "\n"] }
]Output:
{"title":"Galway","artist":"Kevin MacLeod","album":null,"date":null,"track":null,"genre":null,"comment":null,"composer":null,"copyright":null,"language":null}
Scans the input for file signatures and cuts out every embedded file it finds — file carving. Unlike Detect File Type, which only asks what the input starts with, this searches the whole buffer, so it recovers files appended to one another, embedded in a document, or left in slack space.
Because it produces several files rather than one output stream, it must be the
last step in a recipe and needs --out-dir to write them into. Each file is
named extracted_at_0x<offset>.<extension>, where the offset is where its
signature matched.
Signatures are recognized for around 140 formats, but only some can be cut out: carving needs an algorithm that knows where that format ends. A recognized format with no carving algorithm is passed over silently.
The 34 that can be carved are the same set CyberChef advertises, each listed under every extension it goes by:
JPG,JPEG,JPE,THM,MPO |
ZIP |
EVT |
GIF |
TAR |
EVTX |
PNG |
GZ |
DMP |
WEBP |
BZ2 |
PF |
BMP |
ZLIB |
PLIST |
ICO |
XZ |
KEYCHAIN |
TGA |
JAR |
LNK |
FLV |
LZOP,LZO |
DOCX,XLSX,PPTX |
WAV |
DEB |
EPUB |
MP3 |
SQLITE |
DYLIB |
PDF |
EXE,DLL,DRV,VXD,SYS,OCX,VBX,COM,FON,SCR |
|
RTF |
ELF,BIN,AXF,O,PRX,SO |
Several of these share one algorithm — a DOCX, EPUB and JAR are all ZIP
archives, and every name in the EXE row is a Windows portable executable — so
the 34 entries are carved by 32 distinct algorithms.
One caveat. PF covers two formats: the pre-Windows 10 prefetch file, which
records its length and is carved exactly, and the Windows 10 one, which is
compressed and records only the size its contents take once expanded. Nothing in
the latter says how long the compressed data is, so its end cannot be found
without decompressing it; cchef reports that rather than guessing. CyberChef
advertises it too, but reads the file's MAM signature as a big-endian length
and fails out of bounds.
| Option | Type | Default | Notes |
|---|---|---|---|
--images |
boolean | true |
Search for image signatures. |
--video |
boolean | true |
Search for video signatures. |
--audio |
boolean | true |
Search for audio signatures. |
--documents |
boolean | true |
Search for document signatures. |
--applications |
boolean | true |
Search for executable signatures. |
--archives |
boolean | true |
Search for archive and compressed-stream signatures. |
--miscellaneous |
boolean | false |
Off by default: these signatures are short and match often by chance. |
--ignore-failed-extractions |
boolean | true |
When off, a signature that matches but cannot be carved is reported as an error instead of being dropped. |
--minimum-file-size |
number | 100 |
Carved files smaller than this are discarded, which prunes small false positives. |
Note that the same bytes can match more than one signature, and a signature can
match inside another file's data — an archive's members are often found as
separate streams. Both are expected: carving reports candidates, and the
Minimum File Size floor is the first line of defence against noise.
Given report.bin, a PNG with a ZIP archive appended to it:
cchef extract-files --in-file report.bin --out-dir carvedOutput:
carved/extracted_at_0x0.png
carved/extracted_at_0x159.zip
carved/extracted_at_0x1d5.zip
Both the archive and the second member's local header inside it are reported; the first is the whole archive.
Restrict the scan to images, so the archive is passed over:
cchef extract-files --in-file report.bin --out-dir carved --archives=false --documents=false --applications=false --audio=false --video=falseOutput:
carved/extracted_at_0x0.png
Reads the ID3 metadata tag an MP3 file can carry — title, artist, album, track number and so on — and reports it as JSON.
The output names the tag version, its flags and its length, then each frame it holds under the four-character (or, in ID3v2.2, three-character) identifier the format uses, with the identifier's meaning and the frame's contents.
A frame's Data is the frame's bytes with the first left out, since that byte
says how the rest is encoded rather than being part of it, and the remaining
bytes are reported as written rather than decoded. Text frames are terminated
with a null byte, which is why most values end in \u0000.
All three tag versions are read. Note that the lengths are stored differently between them: the tag's own length is always written as seven-bit groups, and so are frame lengths from ID3v2.4, but ID3v2.2 and ID3v2.3 write frame lengths as ordinary integers. CyberChef reads every length as seven-bit groups, so it cannot read an ID3v2.3 frame of 128 bytes or more, nor any tag over 16 KB; cchef reads each as the format specifies.
This operation takes no options.
cchef extract-id3 --in-file tagged.mp3Output:
{"Type":"ID3","Version":"4.0","Flags":"0","Size":"130","Tags":{"TIT2":{"Size":"12","Description":"Title/songname/content description","Data":"Test Title\u0000"},"TPE1":{"Size":"13","Description":"Lead performer(s)/Soloist(s)","Data":"Test Artist\u0000"},"TALB":{"Size":"12","Description":"Album/Movie/Show title","Data":"Test Album\u0000"},"TDRC":{"Size":"6","Description":"Recording time","Data":"2026\u0000"},"TRCK":{"Size":"3","Description":"Track number/Position in set","Data":"3\u0000"},"TSSE":{"Size":"14","Description":"Software/Hardware and settings used for encoding","Data":"Lavf61.1.100\u0000"}}}
The output is compact JSON, so pipe it through JSON Beautify to read it:
cchef extract-id3 --in-file tagged.mp3 | cchef json-beautifyOutput:
{
"Type": "ID3",
"Version": "3.0",
"Flags": "0",
"Size": "66",
"Tags": {
"TIT2": {
"Size": "7",
"Description": "Title/songname/content description",
"Data": "Small\u0000"
},
"TPE1": {
"Size": "5",
"Description": "Lead performer(s)/Soloist(s)",
"Data": "Ann\u0000"
},
"TSSE": {
"Size": "14",
"Description": "Software/Hardware and settings used for encoding",
"Data": "Lavf61.1.100\u0000"
}
}
}
Finds the IPv4 and IPv6 addresses in the input, one per line.
IPv4 is matched in decimal (four groups of 0–255) and in octal (four groups
written with a leading zero); an address is one form or the other, not a mixture.
Digits either side of a match are excluded, so 1.2.3.4.5.6.7.8 gives two
addresses rather than several overlapping ones — but as the operation's own
warning says, that means the reading may not be the one you intended, so check
the original.
| Option | Type | Default | Notes |
|---|---|---|---|
--ipv4 |
boolean | true |
Match IPv4 addresses. |
--ipv6 |
boolean | false |
Match IPv6 addresses. |
--remove-local-ipv4-addresses |
boolean | false |
Leave out the private ranges (10., 172.16–31., 192.168.) and the loopback range (127.). |
--display-total |
boolean | false |
Put a Total found: N line before the results. |
--sort |
boolean | false |
Order by the number the four parts make, so 9.0.0.1 comes before 10.0.0.1. |
--unique |
boolean | false |
Keep one of each. |
With neither version selected the output is empty.
Two things to know about the IPv6 pattern, both inherited from CyberChef and
confirmed against it. The check that an address shortens its run of zeros only
once looks ahead through the rest of the input rather than just the address, so
in fe80::1 and ::1 only the second is found. And matching is done with a
back-tracking engine rather than Go's default one, because the pattern needs
look-behind and back-references that the default engine cannot express.
cchef extract-ip-addresses -i "Server 8.8.8.8 talked to 10.0.0.5 and 2001:db8::1"Output:
8.8.8.8
10.0.0.5
IPv6 is off by default, which is why 2001:db8::1 is not listed.
Drop the private addresses, keep one of each, and count what is left:
cchef extract-ip-addresses -i "8.8.8.8 10.0.0.5 192.168.1.1 8.8.8.8" --remove-local-ipv4-addresses --unique --display-totalOutput:
Total found: 1
8.8.8.8
Pulls MAC addresses out of the input: six pairs of hexadecimal digits separated throughout by colons or by hyphens.
| Option | Type | Default | Notes |
|---|---|---|---|
--display-total |
boolean | false |
Puts a Total found: N line before the results. |
--sort |
boolean | false |
Orders the addresses by value rather than as text, so 0a: comes before ff: and an address written with hyphens falls next to the same address written with colons. |
--unique |
boolean | false |
Keeps one of each. The comparison is exact, so AA:BB:… and aa:bb:… are two different results. |
Separators may not be mixed within one address, and a run of more than six pairs
is matched from its start, so 01:23:45:67:89:ab:cd yields 01:23:45:67:89:ab.
cchef extract-mac-addresses -i "iface eth0 00:1B:44:11:3A:B7, iface eth1 00-1b-44-11-3a-b8"Output:
00:1B:44:11:3A:B7
00-1b-44-11-3a-b8
cchef extract-mac-addresses -i "ff:ff:ff:ff:ff:ff 0a:0b:0c:0d:0e:0f ff:ff:ff:ff:ff:ff" --sort --unique --display-totalOutput:
Total found: 2
0a:0b:0c:0d:0e:0f
ff:ff:ff:ff:ff:ff
Pulls URLs out of the input. The protocol is required — without it almost any dotted word would qualify, and the results would be mostly noise. Use Extract domains to find host names on their own.
| Option | Type | Default | Notes |
|---|---|---|---|
--display-total |
boolean | false |
Puts a Total found: N line before the results. |
--sort |
boolean | false |
Orders the URLs ignoring case. |
--unique |
boolean | false |
Keeps one of each, comparing exactly. |
A path may contain a full stop, comma, exclamation mark or question mark, but not
as its last character, so a URL written at the end of a sentence does not take the
sentence's punctuation with it. A closing bracket, on the other hand, is an
ordinary path character: (https://example.com/a) yields https://example.com/a).
cchef extract-urls -i "Docs at https://example.com/guide?v=2, mirror ftp://files.example.org:2121/pub."Output:
https://example.com/guide?v=2
ftp://files.example.org:2121/pub
cchef extract-urls -i "http://b.example.com http://a.example.com/x http://b.example.com" --sort --unique --display-totalOutput:
Total found: 2
http://a.example.com/x
http://b.example.com
Pulls fully qualified domain names out of the input. Paths are not included — use Extract URLs for those.
| Option | Type | Default | Notes |
|---|---|---|---|
--display-total |
boolean | false |
Puts a Total found: N line before the results. |
--sort |
boolean | false |
Orders the names ignoring case. |
--unique |
boolean | false |
Keeps one of each, comparing exactly, so example.com and Example.com both survive. |
--underscore-dmarc-dkim-etc |
boolean | false |
Allows _ in a label, which is how the records DMARC and DKIM publish are named. |
A name needs at least two labels, each no longer than 63 characters, and a
top-level label of two or more letters — so example.co.uk is found and foo.c
and localhost are not. Internationalised names are matched in their xn--
form; a name written in its own script is not, since the pattern is limited to
letters, digits and hyphens.
cchef extract-domains -i "Visit www.example.com or example.co.uk; not foo.c or localhost."Output:
www.example.com
example.co.uk
Without --underscore-dmarc-dkim-etc, a name such as _dmarc.example.com is
reported as example.com, since the underscore ends the match:
cchef extract-domains -i "_dmarc.example.com and sel._domainkey.example.com" --underscore-dmarc-dkim-etc --sort --display-totalOutput:
Total found: 2
_dmarc.example.com
sel._domainkey.example.com
Pulls email addresses out of the input.
| Option | Type | Default | Notes |
|---|---|---|---|
--display-total |
boolean | false |
Puts a Total found: N line before the results. |
--sort |
boolean | false |
Orders the addresses ignoring case. |
--unique |
boolean | false |
Keeps one of each, comparing exactly. |
Both halves accept the whole range of characters above U+00A0, so an
internationalised address such as 用户@例子.广告 is found as readily as an ASCII
one. The part before the @ may also be a quoted string, which is how an address
holding a character that is otherwise not allowed — including a second @ — is
written. The part after may be a bracketed IPv4 address, checked to be in range:
example@[127.0.0.1] is an address, example@[1.2.3.] is not.
cchef extract-email-addresses -i 'Contact bob@example.com or "very.unusual@strange"@example.org; not a@ or @b.com.'Output:
bob@example.com
"very.unusual@strange"@example.org
Sorting ignores case, so two spellings of one address sort together; uniquing does not, so both are kept:
cchef extract-email-addresses -i "Zoe@Example.com, adam@example.com, zoe@example.com" --sort --unique --display-totalOutput:
Total found: 3
adam@example.com
Zoe@Example.com
zoe@example.com
Pulls anything shaped like a Windows or UNIX path out of the input.
| Option | Type | Default | Notes |
|---|---|---|---|
--windows |
boolean | true |
Look for paths starting at a drive letter. |
--unix |
boolean | true |
Look for slash-separated paths. |
--display-total |
boolean | false |
Puts a Total found: N line before the results. |
--sort |
boolean | false |
Orders the paths ignoring case. |
--unique |
boolean | false |
Keeps one of each, comparing exactly. |
With neither shape asked for the output is empty, and the total is not shown either.
Both shapes are deliberately loose, and the UNIX one especially so: any run of slash-separated words qualifies, and a Windows name may contain spaces, so a path mentioned mid-sentence tends to take some of the sentence with it. This is CyberChef's behavior and the reason the operation warns about false positives — read the results against the original input rather than as a list of real paths.
A Windows path ends in at most one extension of up to six characters, so
data.tar.gz is reported as data.tar.
cchef extract-file-paths -i "Logs in C:\Users\me\logs\app.log and /var/log/syslog"Output:
C:\Users\me\logs\app.log
/var/log/syslog
Either shape can be looked for on its own:
cchef extract-file-paths -i "C:\Users\me\notes.txt and /var/log/syslog" --windows=falseOutput:
/var/log/syslog
And this is what the looseness looks like — the Windows match runs on past the path into the following words, and the UNIX one keeps the full stop that ends the sentence:
cchef extract-file-paths -i "Open C:\Windows\System32\drivers\etc\hosts now, or /etc/passwd."Output:
C:\Windows\System32\drivers\etc\hosts now
/etc/passwd.
Pulls runs of lowercase hexadecimal of a given length out of the input — the shape a hash is usually written in. Nothing is verified: a run of the right length and the right characters is reported whatever it actually is.
| Option | Type | Default | Notes |
|---|---|---|---|
--hash-character-length |
number | 40 |
How many characters a run must have. Ignored when All hashes is on. |
--all-hashes |
boolean | false |
Look for every length in common use instead: 1, 2, 4, 8, 16, 32, 40, 48, 56, 64, 80, 96, 128 and 256 characters, shortest first. |
--display-total |
boolean | false |
Puts a Total Results: N line before the results. |
Only lowercase hexadecimal is matched, so an uppercase digest is not found. Each
run must stand on its own — part of a longer run is not reported as a shorter
hash — which is also why a length that finds nothing under All hashes costs
nothing: the results simply come out grouped by length.
Fidelity. A hash character length below 1 finds nothing here. CyberChef accepts 0, which turns the pattern into one matching the empty string and fills the output with blank lines; every other unusable length already finds nothing there, so this brings 0 into line with them. Negative and fractional lengths find nothing in both.
cchef extract-hashes -i "MD5: 9e107d9d372bb6826bd81d3542a419d6" --hash-character-length 32Output:
9e107d9d372bb6826bd81d3542a419d6
cchef extract-hashes -i "crc 1a2b md5 9e107d9d372bb6826bd81d3542a419d6 sha1 2fd4e1c67a2d28fced849ee1bb76e7391b93eb12" --all-hashes --display-totalOutput:
Total Results: 3
1a2b
9e107d9d372bb6826bd81d3542a419d6
2fd4e1c67a2d28fced849ee1bb76e7391b93eb12
Extracts values from a JSON document using a JSONPath
query, serializing each matched value and joining them with a delimiter. Matched
values are serialized like jsonpath-plus,
including ECMAScript object key ordering.
Supported syntax: root $, child .name / ['name'], wildcard * / [*],
recursive descent .., array index and index-union [0,2], slices [start:end:step],
filters [?(@.price < 10 && @.name == "x")], and script expressions [(@.length-1)].
The magic .length property yields the length of an array or string.
Alternative to
jqfor path-style extraction. The query language is JSONPath, not jq's; see Jq for the jq language itself.
| Option | Type | Default | Notes |
|---|---|---|---|
--query |
string | (empty) | The JSONPath query. |
--result-delimiter |
string | \n |
Joins the serialized matches. Backslash escapes are interpreted (\n → newline, \t → tab). |
Invalid input is reported as Invalid input JSON: <message>; a malformed query as
Invalid JPath expression: <message>.
cchef jpath-expression -i '{"store":{"books":[{"title":"Go"},{"title":"Rust"}]}}' --query "$.store.books[*].title"Output:
"Go"
"Rust"
Filter by a predicate and join with , :
cchef jpath-expression -i '{"books":[{"title":"Cheap","price":5},{"title":"Pricey","price":25},{"title":"Mid","price":9}]}' --query '$..books[?(@.price<10)].title' --result-delimiter ", "Output:
"Cheap", "Mid"
Queries and reshapes a JSON document with a JSONata
expression — a language for selecting, filtering, computing over and rebuilding
JSON, in the spirit of XPath or jq.
The result is written back out as JSON, so a selected string comes back quoted. An expression that selects nothing gives an empty string.
| Option | Type | Default | Notes |
|---|---|---|---|
--query |
string | string |
The JSONata expression. |
Input that is not a JSON document is reported as Invalid input JSON, and an
expression that cannot be read or run as Invalid Jsonata Expression.
Fidelity. CyberChef calls the reference JavaScript implementation; cchef uses
gnata, a Go implementation of JSONata 2.x.
Of CyberChef's 45 test cases, 37 match exactly and 6 more agree in every value
and type but write an object's keys in a different order — gnata keeps the order
internally but does not expose it. Two do not yet come out right: an expression
dividing one indexed value by another (Numbers[0] / Numbers[4]), which gnata
misreads the / in, and one case where the values of an object come back in a
different order.
cchef jsonata-query -i '{"FirstName":"Fred","Surname":"Smith","Age":28}' --query "Surname"Output:
"Smith"
Filter a list and select a field from what is left:
cchef jsonata-query -i '{"Phone":[{"type":"home","number":"0203 544 1234"},{"type":"office","number":"01962 001234"},{"type":"mobile","number":"077 7700 1234"}]}' --query 'Phone[type="mobile"].number'Output:
"077 7700 1234"
Rapid Automatic Keyword Extraction: scores the phrases of a piece of text and lists them, highest first.
The text is split into sentences, and each sentence into the runs of words between its stop words — those runs are the candidate phrases. A word scores by how many words it shares a phrase with, divided by how often it occurs, which favors words appearing in longer phrases over words that merely appear often. A phrase scores as the sum of its words.
The output has two columns, a score and a phrase, under a heading row, so it can be fed straight to To Table.
| Option | Type | Default | Notes |
|---|---|---|---|
--word-delimiter-regex |
string | \s |
Splits a sentence into words. |
--sentence-delimiter-regex |
string | \.\s|\n |
Splits the text into sentences. |
--stop-words |
string | the NLTK list | Comma-separated. Spaces are ignored, and matching is case-insensitive. A stop word ends the phrase it appears in. |
Note that the words are not stripped of punctuation, so a phrase at the end of a sentence keeps the full stop that ended it.
cchef rake -i "Compatibility of systems of linear constraints over the set of natural numbers."Output (first rows):
Scores: , Keywords:
4, linear constraints
4, natural numbers.
1, compatibility
1, systems
1, set
Split on commas rather than spaces, with a stop-word list of your own:
The heading line ends with a space, which CyberChef puts there and cchef keeps; take care not to strip it when editing this page.
cchef rake -i "alpha,beta,the,gamma" --word-delimiter-regex "," --sentence-delimiter-regex "\n" --stop-words "the"Output:
Scores: , Keywords:
4, alpha beta
1, gamma
Finds the runs of readable characters in binary data — the same idea as the Unix
strings command, with more control over what counts as readable.
A run has to be at least Minimum length characters long to be reported. What counts as a character is set by Match, and how those characters are laid out in the bytes is set by Encoding.
Alternative to
strings. cchef adds encoding and regex optionsstringslacks; the default minimum length and character set follow CyberChef, notstrings.
| Option | Type | Default | Notes |
|---|---|---|---|
--encoding |
option | Single byte |
Single byte, 16-bit littleendian, 16-bit bigendian, or All. The wide encodings look for text stored two bytes to the character by allowing a null byte on the appropriate side of each one; All allows one on either side, so it finds both kinds — and will run a one-byte and a two-byte region together into a single result. |
--minimum-length |
number | 4 |
Shorter runs are passed over. |
--match |
option | Alphanumeric + punctuation (A) |
See below. |
--display-total |
boolean | false |
Put a Total found: N line before the results. |
--sort |
boolean | false |
Order the results, ignoring case. |
--unique |
boolean | false |
Keep one of each. |
The six kinds of run, three defined over ASCII and three over Unicode:
| Match | Takes |
|---|---|
Alphanumeric + punctuation (A) |
Letters, digits and the common punctuation. |
All printable chars (A) |
Everything from space to ~. |
Null-terminated strings (A) |
As above, and the run must end with a null byte, which is included. |
Alphanumeric + punctuation (U) |
Any Unicode letter, number, punctuation or separator. |
All printable chars (U) |
Also marks and symbols, so currency signs and the like are kept. |
Null-terminated strings (U) |
As above, ending with a null byte. |
CyberChef's Match list also carries [ASCII] and [Unicode] headings naming the
two groups. They are not choices — picking one leaves the pattern with no
characters to repeat — so cchef offers only the six, as it does elsewhere for
grouped options.
One thing to know about how the input is read. The bytes are taken as UTF-8 when the whole input is valid UTF-8, and as one character per byte when it is not. Which of the two applies changes what counts as a letter, so adding a single stray byte anywhere can change the reading of accented characters throughout. This is CyberChef's behavior and cchef matches it.
Given demo.bin, which holds Hello, a null, wor, a null, then Testing123!:
cchef strings --in-file demo.binOutput:
Hello
Testing123!
wor is left out because it is shorter than the default minimum of four.
The Unicode kinds differ over what is punctuation and what is a symbol. A currency sign is a symbol, so it breaks a run under one and not the other:
cchef strings -i "Grüße€from€Köln" --match "Alphanumeric + punctuation (U)" --minimum-length 4Output:
Grüße
from
Köln
cchef strings -i "Grüße€from€Köln" --match "All printable chars (U)" --minimum-length 4Output:
Grüße€from€Köln
Renders a Handlebars template against JSON input.
Values are written with {{name}}, which escapes them for HTML, or {{{name}}},
which does not. Nested values are reached with dots ({{a.b}}), and a list is
indexed the same way ({{xs.0}}).
| Option | Type | Default | Notes |
|---|---|---|---|
--template-definition-handlebars |
string | (empty) | The template. |
The block helpers, and the values each makes available:
| Written | Does |
|---|---|
{{#if x}}…{{else}}…{{/if}} |
Renders one side or the other. Absent, false, 0, "" and an empty list all count as not present. |
{{#unless x}}…{{/unless}} |
The other way round. |
{{#each xs}}…{{else}}…{{/each}} |
Once per item of a list or field of an object, with {{this}}, {{@index}}, {{@key}}, {{@first}} and {{@last}}. The alternative renders when there is nothing to walk. |
{{#with o}}…{{/with}} |
Renders the body against o. |
{{#*inline "name"}}…{{/inline}} then {{> name}} |
Defines a piece of template and uses it by name. A partial written on an indented line keeps that indentation on every line it renders. |
Inside a block, {{../x}} reads the enclosing context and {{@root.x}} the
document as a whole; a value of the enclosing block is written {{@../index}}.
{{! … }} is a comment. A block tag, comment or partial alone on its line takes
that line with it, so a template written over several lines does not fill the
output with blank ones.
Fidelity. This covers the part of the Handlebars language a template can use
against JSON input. What it does not cover is everything needing a host language —
custom helpers, subexpressions, block
parameters and partial parameters — none of which CyberChef gives a way to
supply. It is also more forgiving than Handlebars about a few malformed
templates: {{../@index}}, for instance, renders nothing here where Handlebars
reports a parse error.
cchef template -i '{"name":"world"}' --template-definition-handlebars 'Hello, {{name}}!'Output:
Hello, world!
Walk a list, and mark the last item:
cchef template --in-file users.json --template-definition-handlebars '{{#each users}}
- {{name}} ({{age}}){{#if @last}} — last{{/if}}
{{/each}}'with users.json:
{"users":[{"name":"Ada","age":36},{"name":"Grace","age":45}]}Output:
- Ada (36)
- Grace (45) — last
Note the escaping, which is what keeps a rendered template plain text:
cchef template -i '{"c":"<b>&</b>"}' --template-definition-handlebars '{{c}} vs {{{c}}}'Output:
<b>&</b> vs <b>&</b>
Extracts nodes from an XML document using an XPath 1.0 query, serializing each selected node and joining the results with a delimiter. It parses the document the same way as CSS selector and evaluates the query with XPath 1.0 semantics.
Only node-set queries are supported, matching the original: a query that
evaluates to a number, string or boolean (e.g. count(//a), string(//a),
1+2) is rejected with Invalid XPath. Details:\nCannot convert <type> to nodeset.. Selected nodes are serialised with the same node.toString() rules —
elements as their markup, attributes as name="value" (with a leading space),
text as its escaped content, comments as <!--…-->, and CDATA as
<![CDATA[…]]>. As with CSS selector, the document is parsed as XML with a single
root element.
| Option | Type | Default | Notes |
|---|---|---|---|
--xpath |
string | (empty) | The XPath 1.0 query. Must evaluate to a node-set. |
--result-delimiter |
string | \n |
Joins the serialized matches. Backslash escapes are interpreted (\n → newline, \t → tab). |
The rarely-used
processing-instruction()node test is not filtered by the underlying engine (it matches every node); every other node test, includingcomment(), behaves as CyberChef does.
cchef xpath-expression -i "<r><a>one</a><a>two</a></r>" --xpath "//a"Output:
<a>one</a>
<a>two</a>
Select the <title> of the <book> whose id attribute is 2, using an
attribute predicate:
cchef xpath-expression -i '<books><book id="1"><title>Go</title></book><book id="2"><title>Rust</title></book></books>' --xpath '//book[@id="2"]/title' --result-delimiter " | "Output:
<title>Rust</title>