ffe is a versatile command-line tool for extracting, parsing, filtering, transforming, and anonymizing fields from text flat files (fixed-length or delimited/CSV) and binary files.
Input formats and arbitrary output formatting are defined using an intuitive configuration file. ffe can read standard input and write to standard output, making it an ideal component for UNIX pipelines, ETL jobs, and data migration workflows.
- Features
- Installation
- Quick Start Tutorial
- Command-Line Options
- Configuration Overview
- Documentation & Future Conversions
- License & Author
- Multiple Input Formats:
- Fixed-length: Flat files where fields occupy constant byte counts.
- Separated / Delimited: CSV, TSV, or arbitrary character-separated files (handles multi-character separators and quotation rules).
- Binary: Native binary structs, fixed and variable-length, with full endianness awareness (little, big, or system endian).
- Multi-Record Support: A single file can contain several distinct record types (e.g., headers, body items, and trailers) identified by positions, strings, or regular expressions (
id/rid). - Flexible Output Formatting: Output is completely independent of input structure. Produce XML, JSON, SQL statements, custom CSV, fixed-length reports, or human-readable tables using
printf-style directives. - Record Filtering & Search:
- Match field contents against strings, numbers, or regular expressions (
=,!,<,>,^,~,#,?). Numerical comparison is used for<and>when both operands are numbers, otherwisestrcmp. - Read comparison values from external files (
file:list.txt). - Combine expressions with logical OR or AND (
-a), with optional case-insensitivity (-X).
- Match field contents against strings, numbers, or regular expressions (
- Data Anonymization Engine: Built-in non-reversible anonymization powered by Libgcrypt (
MASK,RANDOM,NRANDOM,HASH,NHASH), preserving original field lengths and data types. - External Pipes & Lookups: Pipe individual field values through external commands (
filter) or translate codes using lookup tables (lookup). - Automatic Structure Guessing: Automatically identifies the matching structure definition when
-sis omitted. - Transparent Preprocessing: Specify input preprocessors via the
FFEOPENenvironment variable (e.g.,lesspipeto process compressed.gzor.bz2files directly). - Command Substitution: Embed dynamic values in configuration files using backtick syntax (
`command`).
To build ffe, you need a C99 compiler, GNU Autotools, and Libgcrypt:
- Debian / Ubuntu:
sudo apt-get install build-essential autoconf automake texinfo libgcrypt20-dev
- Fedora / RHEL / Rocky Linux:
sudo dnf install gcc make autoconf automake texinfo libgcrypt-devel
- macOS (Homebrew):
brew install autoconf automake texinfo libgcrypt
git clone https://github.com/TimoSavi/ffe.git
cd ffe
autoreconf -is
./configure
make
sudo make installTo run the test suite and verify the build:
make checkConfiguration is read by default from ~/.fferc (or from a custom file using -c).
Suppose you have a fixed-length file named personnel:
john Ripper 23
Scott Tiger 45
Mary Moore 41
Define the structure and desired XML output in ~/.fferc:
structure personnel_file {
type fixed
record person {
field Firstname 9
field Lastname 13
field Age 2
}
}
output xml_format {
file_header "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<people>\n"
file_trailer "</people>\n"
record_header " <person>\n"
record_trailer " </person>\n"
data " <%n>%d</%n>\n"
}
Run ffe:
ffe -s personnel_file -p xml_format personnelOutput:
<?xml version="1.0" encoding="UTF-8"?>
<people>
<person>
<Firstname>john</Firstname>
<Lastname>Ripper</Lastname>
<Age>23</Age>
</person>
<person>
<Firstname>Scott</Firstname>
<Lastname>Tiger</Lastname>
<Age>45</Age>
</person>
<person>
<Firstname>Mary</Firstname>
<Lastname>Moore</Lastname>
<Age>41</Age>
</person>
</people>For a comma-separated file data.csv:
101,John Doe,Developer,75000
102,Jane Smith,Manager,90000Add this structure to your config:
structure employee_csv {
type separated ,
record employee {
field Id
field Name
field Role
field Salary
}
}
Print specific fields:
ffe -s employee_csv -f Name,Role data.csvSelect records using the -e / --expression option (field<op>value):
| Operator | Meaning | Description |
|---|---|---|
= |
Equal | Field equals value |
! |
Not equal | Field does not equal value |
< |
Less than | Numeric comparison if both sides are numbers, otherwise strcmp |
> |
Greater than | Numeric comparison if both sides are numbers, otherwise strcmp |
^ |
Starts with | Field starts with value |
~ |
Contains | Field contains substring |
# |
Does not contain | Field does not contain substring |
? |
Regexp | POSIX extended regular expression match |
Note
Comparison operators (< and >) do not support multiple values for the same field or file: input. Range filtering is achieved by combining expressions with -a (e.g., -a -e "Age>25" -e "Age<45").
Examples:
- Records where
Ageis greater than 30:ffe -s personnel_file -e "Age>30" personnel - Case-insensitive substring match (
-Xand~):ffe -s personnel_file -X -e "Lastname~ripper" personnel - Match values against an external file:
ffe -s personnel_file -e "Lastname=file:target_names.txt" personnel - Combine conditions with logical AND (
-a) for ranges:ffe -s personnel_file -a -e "Age>25" -e "Age<45" personnel
ffe includes a non-reversible anonymizer suitable for GDPR/PCI-DSS compliance and testing data generation.
In ~/.fferc:
anonymize safe_export {
method Firstname RANDOM
method Lastname RANDOM
method Age MASK 0
}
Run ffe with -A:
ffe -s personnel_file -A safe_export personnel| Option | Long Option | Description |
|---|---|---|
-c FILE |
--configuration=FILE |
Read configuration from FILE (default: ~/.fferc). |
-s STRUCTURE |
--structure=STRUCTURE |
Use structure STRUCTURE (disables auto-guessing). |
-p FORMAT |
--print=FORMAT |
Use output format FORMAT for printing. |
-o FILE |
--output=FILE |
Write output to FILE instead of standard output. |
-f LIST |
--field-list=LIST |
Print only comma-separated fields/constants in LIST. |
-e EXPR |
--expression=EXPR |
Filter records matching expression (field<op>value). |
-a |
--and |
Combine multiple -e expressions using AND (default: OR). |
-X |
--casecmp |
Evaluate expressions case-insensitively. |
-v |
--invert-match |
Invert expression match (print non-matching records). |
-l |
--loose |
Continue processing even if an invalid line is encountered. |
-r F=V |
--replace=FIELD=VAL |
Replace FIELD's content with VAL in the output. |
-d |
--debug |
Write invalid input lines to ffe_error_<pid>.log. |
-I |
--info |
Display structure information from config file and exit. |
-A NAME |
--anonymize=NAME |
Apply anonymization profile NAME. |
-? |
--help |
Display command-line help and exit. |
-V |
--version |
Display version information and exit. |
A structure defines how input data is partitioned into records:
structure <name> {
type fixed | separated <delimiter> | binary
quoted yes | no | <char>
header first | all | no
output <output_name>
record <record_name> { ... }
}
A record defines fields and identification criteria:
record <name> {
id <position> <string> # Exact string identifier
rid <position> <regexp> # Regular expression identifier
field <name> [<length>|<type>] [<lookup>] [<output>] [<filter>] [<conversion>]
field FILLER <length> # Ignored/padding bytes
}
An output format governs how extracted fields are displayed:
output <name> {
file_header <picture> # Printed once before file data
file_trailer <picture> # Printed once after file data
record_header <picture> # Printed before each record
record_trailer <picture> # Printed after each record (default: newline)
data <picture> # Printed for each field
lookup <picture> # Used if field references a lookup table
separator <string> # Field delimiter
indent <string> # Indentation string
field-list <name1>,<name2> # Field subset and ordering
hex-caps yes | no # Uppercase hex numbers
}
Inside output pictures (like data or record_header), the following % directives can be used:
| Directive | Description |
|---|---|
%d |
Field contents formatted according to its type (binary as decimal, float as [-]ddd.ddd, BCD as decimal, hex as hex). |
%t |
Field contents with leading and trailing whitespace trimmed. |
%D |
Field contents right-padded with spaces to the field length. |
%C |
Field contents right-padded to field length; truncated if longer. |
%n |
Name of the current field. |
%l |
Lookup value found using the current field as search key. |
%L |
Lookup value right-padded with spaces to the field length. |
%p |
Field start position (byte position for fixed/binary, field index for separated, starting from 1). |
%x |
Unsigned hexadecimal value of a binary integer. |
%h |
Hexadecimal byte dump of a binary field (xnn representation). |
%e |
Suppresses output but triggers field empty check. |
%s |
Name of the current structure. |
%r |
Name of the current record. |
%o |
Input record number in current file (starting from 1). |
%O |
Input record number starting from the first file. |
%i |
Byte offset of the current record in current file (starting from 0). |
%I |
Byte offset of the current record starting from the first file. |
%g |
Group name (defined by group_name for hierarchical levels). |
%m |
Element name (defined by element_name for hierarchical levels). |
%% |
Literal percent sign (%). |
\n, \t, \r, \" |
Standard escape sequences. |
Anonymization rules are defined with the anonymize block:
anonymize <name> {
method <field> <algorithm> [<start>] [<length>] [<parameter>]
}
Supported algorithms:
MASK: Replace characters with a masking character (default:'0').RANDOM: Replace with pseudo-random characters (0-9,a-z,A-Z, space).NRANDOM: Replace with pseudo-random numeric digits (0-9).HASH: Replace with cryptographic hash digest (Libgcrypt MD5/SHA) maintaining determinism for matching inputs.NHASH: Replace with numeric cryptographic hash.
- Lookup Tables:
lookup country_code { pair "FI" "Finland" pair "SE" "Sweden" default "Unknown" } - Shell Filters:
filter uppercase "tr '[:lower:]' '[:upper:]'"
The authoritative, comprehensive user guide is maintained in GNU Texinfo format in doc/ffe.texi.
When ffe is built, the Texinfo source is compiled into standard formats:
- GNU Info:
info ffe(or viewdoc/ffe.info) - HTML Manual: Open
doc/ffe.htmlin any browser - Man Page:
man ffe(source:doc/ffe.1)
If you update doc/ffe.texi in the future and want to regenerate or update this README.md, you can use either of the following methods:
GNU Texinfo does not natively output Markdown. The standard conversion pipeline converts Texinfo to DocBook XML first, then translates DocBook into GitHub-Flavored Markdown:
-
Install tools (if not already present):
sudo apt-get install texinfo pandoc # Debian/Ubuntu sudo dnf install texinfo pandoc # Fedora/RHEL
-
Run the conversion pipeline:
# Step 1: Export Texinfo source to DocBook XML texi2any --docbook doc/ffe.texi -o doc/ffe.xml # Step 2: Convert DocBook XML to GitHub-Flavored Markdown pandoc -f docbook -t gfm doc/ffe.xml -o README.md # Step 3: Clean up intermediate XML rm doc/ffe.xml
While automated conversion via pandoc produces complete output, it often includes Texinfo menu artifacts, navigation headers, and rigid formatting. For a project README.md, manually or AI-assisted updating from doc/ffe.texi is recommended to ensure:
- Clean GitHub badges and concise tables.
- Modern syntax highlighting blocks (
sh`,xml, ````text). - Direct, task-focused quick start examples.
ffe was written by Timo Savinen (tjsa@iki.fi).
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See the COPYING file for full details.