Table of Contents
- What is JQ?
- Installing JQ
- Basic JQ Concepts
- Core JQ Operations
- Advanced Use Cases
- Best Practices
- Conclusion
- References
What is JQ?
Jq is an open-source command-line tool designed to parse, filter, and transform JSON data. Developed by Stephen Dolan, it acts as a “JSON query language,” allowing you to extract specific fields, filter arrays, modify values, and even generate new JSON structures—all with minimal code.
Key features of jq include:
- Lightweight: Written in C, jq is fast and has minimal dependencies.
- Expressive Syntax: Use filters to chain operations (e.g.,
|,select(),map()). - Flexibility: Works with JSON from files, stdin, or even Bash variables.
- Portable: Available for Linux, macOS, Windows, and BSD.
Installing JQ
Jq is pre-installed on many Linux distributions, but if not, install it using your package manager:
Linux (Debian/Ubuntu)
sudo apt update && sudo apt install jq -y
Linux (RHEL/CentOS/Fedora)
sudo dnf install jq -y # or sudo yum install jq -y
macOS
Use Homebrew:
brew install jq
Windows
- Use Chocolatey:
choco install jq - Or download the binary from the official jq releases.
Verify installation with:
jq --version # Should output something like "jq-1.6"
Basic JQ Concepts
To get started, let’s use a sample JSON file (sample.json) to demonstrate core jq functionality. Save this to your working directory:
{
"company": "TechCorp",
"employees": [
{
"id": 1,
"name": "Alice",
"department": "Engineering",
"age": 32,
"skills": ["Python", "JQ", "Bash"]
},
{
"id": 2,
"name": "Bob",
"department": "Marketing",
"age": 28,
"skills": ["JavaScript", "SEO"]
},
{
"id": 3,
"name": "Charlie",
"department": "Engineering",
"age": 35,
"skills": ["Java", "AWS"]
}
],
"active": true
}
The Dot (.) Filter
The simplest jq filter is the dot (.), which returns the entire JSON input. Use it to test if jq is working:
jq '.' sample.json
Output:
{
"company": "TechCorp",
"employees": [
{
"id": 1,
"name": "Alice",
"department": "Engineering",
"age": 32,
"skills": [
"Python",
"JQ",
"Bash"
]
},
... # Rest of the JSON (pretty-printed)
],
"active": true
}
The . filter also auto-pretty-prints JSON, making it useful for debugging.
Accessing Keys and Values
To extract a specific key, append the key name to the dot filter. For example, to get the company name:
jq '.company' sample.json
Output:
"TechCorp"
Jq returns values as JSON literals by default (e.g., strings in quotes). To get raw output (without quotes), use the -r flag:
jq -r '.company' sample.json # Output: TechCorp
Core JQ Operations
Working with Arrays
JSON arrays are common in datasets (e.g., lists of users or logs). Jq provides simple syntax to interact with arrays:
Access by Index
Extract the first employee (arrays are zero-indexed):
jq '.employees[0]' sample.json
Output:
{
"id": 1,
"name": "Alice",
"department": "Engineering",
"age": 32,
"skills": ["Python", "JQ", "Bash"]
}
Iterate Over Arrays
Use [] to iterate over all elements in an array. For example, list all employee names:
jq '.employees[].name' sample.json
Output:
"Alice"
"Bob"
"Charlie"
Add -r to get raw names:
jq -r '.employees[].name' sample.json
# Alice
# Bob
# Charlie
Filtering Data with select()
Use the select(condition) function to filter arrays based on criteria. For example, find employees in the “Engineering” department:
jq '.employees[] | select(.department == "Engineering")' sample.json
Here, | (pipe) chains operations: first iterate over employees[], then select elements where department equals “Engineering.”
Output:
{
"id": 1,
"name": "Alice",
"department": "Engineering",
"age": 32,
"skills": ["Python", "JQ", "Bash"]
}
{
"id": 3,
"name": "Charlie",
"department": "Engineering",
"age": 35,
"skills": ["Java", "AWS"]
}
You can combine conditions with logical operators (and, or, not). For example, find engineers over 30:
jq '.employees[] | select(.department == "Engineering" and .age > 30)' sample.json
Modifying JSON Structures
Jq isn’t just for reading JSON—it can also modify values, add/remove fields, and reshape data.
Update a Value
Set the active status to false:
jq '.active = false' sample.json
Add a New Field
Add a location field to the company:
jq '.location = "San Francisco"' sample.json
Delete a Field
Remove the active field:
jq 'del(.active)' sample.json
Modify Array Elements
Add a skill to Alice’s list:
jq '.employees[0].skills += ["Docker"]' sample.json
Handling Nested JSON
Real-world JSON is often nested (arrays inside objects inside arrays). Jq excels at traversing these structures. Let’s extend our sample.json to include a nested projects array:
{
"company": "TechCorp",
"employees": [
{
"id": 1,
"name": "Alice",
"department": "Engineering",
"projects": [
{"name": "API Rewrite", "status": "active"},
{"name": "CI/CD Pipeline", "status": "completed"}
]
}
]
}
To list all active projects across all employees:
jq '.employees[].projects[] | select(.status == "active").name' sample.json
Output:
"API Rewrite"
Advanced Use Cases
Combining JQ with Bash Variables
Jq can reference Bash variables using the --arg flag, making scripts dynamic. For example, update an employee’s name using a Bash variable:
EMP_ID=1
NEW_NAME="Alicia"
jq --arg id "$EMP_ID" --arg name "$NEW_NAME" \
'.employees[] | select(.id == ($id | tonumber)).name = $name' sample.json
Here:
--arg id "$EMP_ID"passes the Bash variableEMP_IDto jq as$id.($id | tonumber)converts the string$idto a number (since jq treats--argvalues as strings).
Processing Multiple Files
Jq can process multiple JSON files with the -s (slurp) flag, which combines files into a single array. For example, merge data1.json and data2.json:
jq -s 'add' data1.json data2.json # "add" merges objects/arrays
To loop through files in Bash and process them with jq:
for file in *.json; do
jq '.employees[] | select(.department == "Engineering")' "$file" >> engineers.json
done
Error Handling and Null Safety
Jq includes tools to handle missing keys, null values, and errors gracefully.
Check if a Key Exists
Use has("key") to verify if a field exists:
jq '.employees[0] | has("projects")' sample.json # Output: true
Default Values for Nulls
Use the // operator to provide a default if a value is null or missing:
jq '.employees[0].email // "[email protected]"' sample.json
Try/Catch for Errors
Use try/catch to handle invalid JSON or missing fields:
jq 'try .employees[0].name catch "Unknown"' invalid.json
Stream Processing
For large JSON files (e.g., multi-gigabyte logs), use jq’s streaming mode (--stream) to process data incrementally without loading the entire file into memory:
jq --stream 'select(.[0][1] == "Engineering")' large_logs.json
Best Practices
- Use Raw Output for Strings: Add
-rto avoid quotes when extracting strings (e.g.,jq -r '.name'). - Pretty-Print by Default: Jq auto-pretty-prints, but use
-M(monochrome) for scripts or-C(color) for readability. - Test Filters with jqplay: Use jqplay.org to prototype filters interactively.
- Comment Complex Filters: Add comments with
#(escaped in Bash with\#):jq '.employees[] | select(.age > 30) | # Filter adults {name, department}' # Keep only name and department - Avoid Redundant Loops: Prefer jq’s native array iteration (
.array[]) over Bash loops for performance.
Conclusion
Jq transforms JSON parsing from a tedious chore into a streamlined process. Whether you’re extracting data from APIs, automating config files, or analyzing logs, jq’s expressive syntax and integration with Bash make it a must-have tool. By mastering basics like key access and array filtering, then progressing to advanced topics like variable injection and error handling, you’ll unlock powerful automation workflows.
Start small—experiment with sample.json, then apply jq to your own projects. The more you use it, the more you’ll appreciate how it simplifies complex JSON tasks!
References
- Official Jq Manual
- Jq Installation Guide
- jqplay.org (interactive jq playground)
- Jq GitHub Repository
- Stack Overflow Jq Tag (community examples)
- Bash + Jq Script Examples (GitHub)