pick
Extract values from anything — JSON, YAML, TOML, .env, HTTP headers, logfmt, CSV, and more.
pick auto-detects the input format and lets you extract, filter, and transform values using a unified selector syntax. Think of it as jq for all config formats — no more juggling jq, yq, grep, awk, and cut.
Quick Start
# JSON
|
# octocat
# .env
|
# postgres://localhost:5432/mydb
# YAML
|
# 8080
# TOML
|
# 0.1.0
# HTTP headers
|
# text/html; charset=UTF-8
# logfmt
|
# 200
# CSV
|
# Alice
Selectors
| Syntax | Description |
|---|---|
foo |
Top-level key |
foo.bar |
Nested key |
foo[0] |
Array index |
foo[-1] |
Last element |
foo[*].name |
All elements, pluck field |
foo[1:3] |
Array slice (elements 1 and 2) |
foo[:2] |
First 2 elements |
foo[-2:] |
Last 2 elements |
[0] |
Index into root array |
..name |
Recursive descent — find name at any depth |
"dotted.key".sub |
Quoted key (for keys containing dots) |
name, age |
Multiple selectors (union) |
Pipes & Filters
Chain operations with the pipe operator (|), filter with select(), and transform with builtins:
# Filter: find expensive items
|
# Regex: match patterns
|
# Boolean logic: and, or, not
|
# Builtins: keys, values, length
|
|
|
# Chain multiple stages
|
Filter Operators
| Operator | Example | Description |
|---|---|---|
== |
select(.role == "admin") |
Equality |
!= |
select(.status != "deleted") |
Inequality |
> |
select(.price > 100) |
Greater than |
< |
select(.age < 18) |
Less than |
>= |
select(.score >= 90) |
Greater or equal |
<= |
select(.count <= 10) |
Less or equal |
~ |
select(.name ~ "^A") |
Regex match |
and |
select(.a > 1 and .b < 5) |
Logical AND |
or |
select(.x == 1 or .y == 2) |
Logical OR |
not |
select(not .deleted) |
Logical NOT |
Builtins
| Builtin | Description |
|---|---|
keys() |
Get object keys or array indices |
values() |
Get object values |
length() |
Length of array, object, or string |
Mutation
Modify data in-place with set() and del():
# Set a value
|
# Delete a key
|
# Chain mutations
|
# Mutate then extract
|
Output Formats
Convert between formats with --output:
# JSON to YAML
|
# JSON to TOML
|
# Always output JSON
|
Streaming (JSONL)
Process newline-delimited JSON (JSONL) line-by-line with --stream:
# Extract from each line
|
# Filter streamed data
|
# Stream from file
Flags
| Flag | Description |
|---|---|
-i, --input <format> |
Force input format (json, yaml, toml, env, headers, logfmt, csv, text) |
-o, --output <format> |
Output format (json, yaml, toml) |
-f, --file <path> |
Read from file instead of stdin |
--json |
Output result as JSON |
--raw |
Output without trailing newline |
-1, --first |
Only output first result |
--lines |
Output array elements one per line |
-d, --default <value> |
Default value if selector doesn't match |
-q, --quiet |
Suppress error messages |
-e, --exists |
Check if selector matches (exit code only) |
-c, --count |
Output count of matches |
--stream |
Stream mode: process JSONL input line-by-line |
Examples
Pipe-friendly
# Get the current git user's repos
|
# Check if a key exists before using it
if | ; then
DB_HOST=
fi
# Extract with a fallback
|
# Count results
|
# 5
Real-world
# Docker container status
|
# Kubernetes pod IPs
|
# Find all IDs recursively in any JSON
|
# Cargo.toml dependencies
# GitHub API: filter Rust repos with 100+ stars
| \
# Terraform outputs
|
# npm: count dependencies
|
# .env database URL for a script
Supported Formats
| Format | Auto-detected | Example |
|---|---|---|
| JSON | Yes | {"key": "value"} |
| YAML | Yes | key: value |
| TOML | Yes | [section] / key = "value" |
| .env | Yes | KEY=value |
| HTTP headers | Yes | Content-Type: text/html |
| logfmt | Yes | level=info msg="hello" |
| CSV/TSV | Yes | name,age\nAlice,30 |
| Plain text | Fallback | Key-value extraction and substring search |
Auto-detection works in most cases. Use -i to override when the input is ambiguous.
Install
Cargo (Rust)
Homebrew (macOS/Linux)
npm
Snap (Linux)
Chocolatey (Windows)
choco install pick
WinGet (Windows)
winget install aryanbhosale.pick
Docker
|
GitHub Releases
Download pre-built binaries from Releases — macOS (ARM/x64), Linux (x64/ARM64), and Windows (x64).
From source
Requires Rust 1.85+:
Contributing
Contributions are welcome! Here's how to get started:
- Fork the repository
- Create a feature branch:
git checkout -b my-feature - Make your changes
- Run the tests:
cargo test - Commit and push:
git push origin my-feature - Open a pull request
Development
# Run all tests (866 tests)
# Run a specific test
# Build release binary
# The binary will be at target/release/pick
Project Structure
src/
main.rs Entry point, stdin/file reading, streaming
lib.rs Orchestration and format routing
cli.rs CLI argument definitions
error.rs Error types
selector/ Selector engine (modular)
types.rs AST types (Expression, Pipeline, Selector, Filter, etc.)
parser.rs Hand-rolled recursive descent parser
extract.rs Path traversal and pipeline execution
filter.rs Filter evaluation (select, comparisons, regex)
manipulate.rs set() and del() operations
detector.rs Format auto-detection heuristics
output.rs Output formatting (plain, JSON, YAML, TOML)
streaming.rs JSONL streaming processor
formats/ Per-format parsers
json.rs, yaml.rs, toml_format.rs, env.rs,
headers.rs, logfmt.rs, csv_format.rs, text.rs
tests/
integration.rs CLI integration tests
Issues
Found a bug or have a feature request? Open an issue.