config/modules/home/scripts/opsearch.sh
2025-06-15 18:15:28 -04:00

323 lines
10 KiB
Bash
Executable file

#!/usr/bin/env bash
# opsearch.sh - A tool to search through 1Password items
#
# Usage:
# opsearch <search_term> [options]
#
# Options:
# -c, --category Filter by category (login, password, document, etc.)
# -t, --tag Filter by tag
# -v, --vault Specify vault to search in
# -f, --field Search only in specific fields
# -j, --json Output results in JSON format
# -d, --detail Show detailed output for each item
# -n, --nushell Use Nushell for output formatting (if available)
# -p, --password Retrieve and copy password after selection
# -h, --help Show this help message
set -e
# Default options
FORMAT="json"
SHOW_DETAIL=false
CATEGORY=""
TAG=""
VAULT=""
FIELD=""
SEARCH_TERM=""
USE_NUSHELL=false
GET_PASSWORD=false
# Check if 1Password CLI is available
if ! command -v op &>/dev/null; then
echo "Error: 1Password CLI (op) not found."
echo "Please install it from https://1password.com/downloads/command-line/"
exit 1
fi
# Check if 1Password CLI is signed in
if ! op account get --format=json &>/dev/null; then
echo "You need to sign in to 1Password CLI first."
echo "Run: eval \$(op signin)"
exit 1
fi
# Function to display help
show_help() {
cat << EOF
opsearch - Search 1Password items with grep-like functionality
Usage:
opsearch <search_term> [options]
Options:
-c, --category <category> Filter by category (login, password, document, etc.)
-t, --tag <tag> Filter by tag
-v, --vault <vault> Specify vault to search in
-f, --field <field> Search only in specific fields
-j, --json Output results in JSON format
-d, --detail Show detailed output for each item
-n, --nushell Use Nushell for output formatting (if available)
-p, --password Retrieve and copy password after selection
-h, --help Show this help message
Examples:
opsearch github # Search for "github" in all items
opsearch amazon -c login # Search for "amazon" in login items
opsearch bank -v Personal # Search for "bank" in the Personal vault
opsearch -t finance # Show all items with "finance" tag
opsearch email -f username # Search for "email" in username fields
opsearch ssh -d # Show detailed info for SSH items
opsearch github -p # Get GitHub password after selecting item
EOF
exit 0
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
show_help
;;
-j|--json)
FORMAT="json"
shift
;;
-d|--detail)
SHOW_DETAIL=true
shift
;;
-n|--nushell)
USE_NUSHELL=true
shift
;;
-p|--password)
GET_PASSWORD=true
shift
;;
-c|--category)
CATEGORY="$2"
shift 2
;;
-t|--tag)
TAG="$2"
shift 2
;;
-v|--vault)
VAULT="$2"
shift 2
;;
-f|--field)
FIELD="$2"
shift 2
;;
-*)
echo "Unknown option: $1"
show_help
;;
*)
if [[ -z "$SEARCH_TERM" ]]; then
SEARCH_TERM="$1"
else
echo "Error: Multiple search terms provided. Use quotes for terms with spaces."
exit 1
fi
shift
;;
esac
done
# Check for required tools
check_dependencies() {
if ! command -v jq &> /dev/null; then
echo "Error: jq is required for this script to function properly."
echo "Please install it using your package manager."
exit 1
fi
if [[ "$USE_NUSHELL" == "true" ]] && ! command -v nu &> /dev/null; then
echo "Warning: Nushell not found, falling back to jq for formatting."
USE_NUSHELL=false
fi
if [[ "$GET_PASSWORD" == "true" ]]; then
if ! command -v xclip &> /dev/null && ! command -v pbcopy &> /dev/null && ! command -v wl-copy &> /dev/null; then
echo "Warning: No clipboard utility found (xclip, pbcopy, or wl-copy required)."
echo "Password will be displayed but not copied."
fi
fi
}
# Build the 1Password CLI command
OP_CMD="op item list --format=json"
# Add filters to the command
if [[ -n "$CATEGORY" ]]; then
OP_CMD="$OP_CMD --categories=$CATEGORY"
fi
if [[ -n "$TAG" ]]; then
OP_CMD="$OP_CMD --tags=$TAG"
fi
if [[ -n "$VAULT" ]]; then
OP_CMD="$OP_CMD --vault=$VAULT"
fi
# Function to colorize the output
highlight_match() {
local text="$1"
local pattern="$2"
echo "$text" | grep --color=always -i "$pattern" || echo "$text"
}
# Function to copy to clipboard
copy_to_clipboard() {
local text="$1"
if command -v wl-copy &> /dev/null; then
echo -n "$text" | wl-copy
echo "Copied to clipboard with wl-copy"
elif command -v xclip &> /dev/null; then
echo -n "$text" | xclip -selection clipboard
echo "Copied to clipboard with xclip"
elif command -v pbcopy &> /dev/null; then
echo -n "$text" | pbcopy
echo "Copied to clipboard with pbcopy"
else
echo "No clipboard utility found. Here's the value:"
echo "$text"
fi
}
# Main function to format and display results
display_items() {
local items="$1"
local count=$(echo "$items" | jq 'length')
if [[ $count -eq 0 ]]; then
echo "No items found matching your search criteria."
exit 0
fi
echo "Found $count matching items:"
if [[ "$USE_NUSHELL" == "true" ]]; then
# Use Nushell for pretty output
echo "$items" | nu -c "open - | select id title category updated vault_id | sort-by title"
else
# Use jq for formatting
echo "$items" | jq -r '
["ID", "TITLE", "CATEGORY", "UPDATED"] as $headers |
([$headers] +
(. | map([.id, .title, .category, .updated[0:10]]))) |
.[] | @tsv' | column -t -s $'\t'
fi
if [[ $count -gt 0 ]]; then
echo ""
select_item "$items"
fi
}
# Function to select an item and show details or get password
select_item() {
local items="$1"
local count=$(echo "$items" | jq 'length')
echo "Select an item by number (1-$count) or press Enter to exit:"
select opt in $(echo "$items" | jq -r '.[].title'); do
if [[ -z "$opt" ]]; then
echo "Exiting."
exit 0
fi
local idx=$((REPLY-1))
if [[ $idx -ge 0 && $idx -lt $count ]]; then
local item_id=$(echo "$items" | jq -r ".[$idx].id")
local item_title=$(echo "$items" | jq -r ".[$idx].title")
echo "Selected: $item_title (ID: $item_id)"
if [[ "$GET_PASSWORD" == "true" ]]; then
# Get password and copy to clipboard
local password=$(op item get "$item_id" --fields password)
if [[ -n "$password" ]]; then
copy_to_clipboard "$password"
else
echo "No password field found for this item."
# Show available fields
echo "Available fields:"
op item get "$item_id" --format=json | jq -r '.fields[] | select(.id != "notesPlain") | "\(.id): \(.label)"'
echo "Get a specific field? Enter field ID or press Enter to skip:"
read -r field_id
if [[ -n "$field_id" ]]; then
local field_value=$(op item get "$item_id" --fields "$field_id")
if [[ -n "$field_value" ]]; then
copy_to_clipboard "$field_value"
else
echo "Field not found or empty."
fi
fi
fi
elif [[ "$SHOW_DETAIL" == "true" ]]; then
# Show detailed information
echo "Detailed information:"
echo "------------------------------------------------"
if [[ -n "$FIELD" ]]; then
# Show only the requested field
op item get "$item_id" --fields "$FIELD"
else
# Show all fields
if [[ "$USE_NUSHELL" == "true" ]]; then
op item get "$item_id" --format=json | nu -c "open - | get fields | where id != 'notesPlain' | sort-by label"
else
op item get "$item_id" --format=json | jq -r '.fields[] | select(.id != "notesPlain") | "\(.label): \(.value)"'
fi
fi
echo "------------------------------------------------"
else
# Show basic information
op item get "$item_id" --format=json | jq -r '.fields[] | select(.id == "username" or .id == "password" or .id == "notesPlain") | "\(.label): \(.value // "[Hidden]")"'
fi
echo ""
echo "Select another item or press Ctrl+C to exit:"
else
echo "Invalid selection. Please choose a number between 1 and $count."
fi
done
}
# Execute search
check_dependencies
if [[ -z "$SEARCH_TERM" && -z "$TAG" && -z "$CATEGORY" ]]; then
echo "Error: No search term provided."
show_help
exit 1
fi
# Execute the command and store the results
RESULT=$(eval "$OP_CMD")
# Filter results based on search term
if [[ -n "$SEARCH_TERM" ]]; then
FILTERED_RESULT=$(echo "$RESULT" | jq --arg term "$SEARCH_TERM" -r '[.[] | select(
(.title | ascii_downcase | contains($term | ascii_downcase)) or
(.id | ascii_downcase | contains($term | ascii_downcase)) or
(.additional_information | ascii_downcase | contains($term | ascii_downcase)) or
(.urls != null and (.urls[] | ascii_downcase | contains($term | ascii_downcase))) or
(.tags != null and (.tags[] | ascii_downcase | contains($term | ascii_downcase)))
)]')
else
FILTERED_RESULT="$RESULT"
fi
# Display the results
display_items "$FILTERED_RESULT"