Skip to main content

Scripting with grph CLI

grph CLI is designed to work well in scripts and automation pipelines. This page covers common scripting patterns.

Basic Scripting

Exit Codes

grph CLI uses standard exit codes:

CodeMeaning
0Success
1Error (file not found, parse error, etc.)
if grph info graph.gexf > /dev/null 2>&1; then
echo "Valid GEXF file"
else
echo "Invalid or missing file"
exit 1
fi

Capturing Output

# Capture JSON output
nodes=$(grph nodes graph.gexf --json)
echo "Found $(echo "$nodes" | jq length) nodes"

# Capture specific values
node_count=$(grph info graph.gexf --json | jq '.node_count')
echo "Graph has $node_count nodes"

Shell Scripts

Validate Multiple Files

#!/bin/bash
# validate-graphs.sh

for file in graphs/*.gexf; do
if grph info "$file" > /dev/null 2>&1; then
echo "✓ $file"
else
echo "✗ $file (invalid)"
fi
done

Generate a Report

#!/bin/bash
# graph-report.sh

file="$1"

if [ -z "$file" ]; then
echo "Usage: $0 <file.gexf>"
exit 1
fi

echo "# Graph Report: $file"
echo ""

# Basic info
echo "## Summary"
grph info "$file" --json | jq -r '"- Nodes: \(.node_count)\n- Edges: \(.edge_count)\n- Mode: \(.mode)"'
echo ""

# Node breakdown
echo "## Nodes by Type"
grph nodes "$file" --json | jq -r 'group_by(.attributes.type) | .[] | "- \(.[0].attributes.type // "unknown"): \(length)"'
echo ""

# Top connected nodes
echo "## Most Connected Nodes (Outgoing)"
grph edges "$file" --json | jq -r 'group_by(.source) | sort_by(-length) | .[0:5] | .[] | "- \(.[0].source): \(length) edges"'

Find and Process

#!/bin/bash
# find-servers.sh

file="$1"

# Get all server nodes
servers=$(grph nodes "$file" --attr type=server --json)

# Process each server
echo "$servers" | jq -c '.[]' | while read -r node; do
id=$(echo "$node" | jq -r '.id')
label=$(echo "$node" | jq -r '.label')

# Count connections
outgoing=$(grph edges "$file" --source "$id" --json | jq 'length')
incoming=$(grph edges "$file" --target "$id" --json | jq 'length')

echo "$label ($id): $outgoing outgoing, $incoming incoming"
done

Python Integration

Basic Usage

import subprocess
import json

def get_graph_info(filepath):
"""Get graph info as a dictionary."""
result = subprocess.run(
['gfx', 'info', filepath, '--json'],
capture_output=True,
text=True
)
if result.returncode != 0:
raise RuntimeError(f"Failed to read graph: {result.stderr}")
return json.loads(result.stdout)

def get_nodes(filepath, **filters):
"""Get nodes with optional filters."""
cmd = ['gfx', 'nodes', filepath, '--json']
for key, value in filters.items():
if key == 'label':
cmd.extend(['--label', value])
elif key == 'attr':
for k, v in value.items():
cmd.extend(['--attr', f'{k}={v}'])

result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
raise RuntimeError(f"Failed to get nodes: {result.stderr}")
return json.loads(result.stdout)

# Usage
info = get_graph_info('network.gexf')
print(f"Graph has {info['node_count']} nodes")

servers = get_nodes('network.gexf', attr={'type': 'server'})
for server in servers:
print(f"Server: {server['label']}")

Analysis Script

#!/usr/bin/env python3
"""Analyze a GEXF graph file."""

import subprocess
import json
import sys
from collections import Counter

def run_gfx(cmd):
"""Run a grph command and return JSON output."""
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"Error: {result.stderr}", file=sys.stderr)
sys.exit(1)
return json.loads(result.stdout)

def analyze_graph(filepath):
"""Analyze a graph and print statistics."""
info = run_gfx(['gfx', 'info', filepath, '--json'])
nodes = run_gfx(['gfx', 'nodes', filepath, '--json'])
edges = run_gfx(['gfx', 'edges', filepath, '--json'])

print(f"Graph: {filepath}")
print(f" Nodes: {info['node_count']}")
print(f" Edges: {info['edge_count']}")
print()

# Node type distribution
type_counts = Counter(n['attributes'].get('type', 'unknown') for n in nodes)
print("Node Types:")
for node_type, count in type_counts.most_common():
print(f" {node_type}: {count}")
print()

# Edge relationship distribution
rel_counts = Counter(e['attributes'].get('relationship', 'unknown') for e in edges)
print("Edge Relationships:")
for rel, count in rel_counts.most_common():
print(f" {rel}: {count}")
print()

# Degree analysis
out_degree = Counter(e['source'] for e in edges)
in_degree = Counter(e['target'] for e in edges)

print("Top 5 by Outgoing Edges:")
for node_id, count in out_degree.most_common(5):
node = next((n for n in nodes if n['id'] == node_id), None)
label = node['label'] if node else node_id
print(f" {label}: {count}")

if __name__ == '__main__':
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <file.gexf>")
sys.exit(1)
analyze_graph(sys.argv[1])

CI/CD Integration

GitHub Actions

name: Validate Graphs

on: [push, pull_request]

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install gfx-cli
run: pip install gfx-cli

- name: Validate graph files
run: |
for file in data/*.gexf; do
echo "Validating $file..."
grph info "$file"
done

Pre-commit Hook

#!/bin/bash
# .git/hooks/pre-commit

# Validate any GEXF files being committed
gexf_files=$(git diff --cached --name-only --diff-filter=ACM | grep '\.gexf$')

if [ -n "$gexf_files" ]; then
echo "Validating GEXF files..."
for file in $gexf_files; do
if ! grph info "$file" > /dev/null 2>&1; then
echo "Error: Invalid GEXF file: $file"
exit 1
fi
echo " ✓ $file"
done
fi

Tips

Performance

For large files, prefer filtered queries over fetching everything:

# Slower: fetch all, then filter
grph nodes large.gexf --json | jq '[.[] | select(.attributes.type == "server")]'

# Faster: filter at source
grph nodes large.gexf --attr type=server --json

Error Messages

Redirect stderr to capture error messages separately:

output=$(grph nodes graph.gexf --json 2>&1)
if [ $? -ne 0 ]; then
echo "Error: $output"
fi

Parallel Processing

Process multiple files in parallel:

# Using GNU parallel
find graphs/ -name "*.gexf" | parallel 'grph info {} --json > {.}.info.json'

# Using xargs
find graphs/ -name "*.gexf" -print0 | xargs -0 -P4 -I{} sh -c 'grph info "$1" --json' _ {}