Last active
April 13, 2025 15:40
-
-
Save SWORDIntel/6372ba36768a7aa1c454854b0dcd10b2 to your computer and use it in GitHub Desktop.
extract
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# ---------------------------------------------------------------------------- | |
# Script: file_extract.sh | |
# | |
# Description: | |
# This script allows you to interactively select a file from the current | |
# directory using fzf (a terminal-based fuzzy finder), then prompts you to | |
# input a search string (which can be a regex or plain text). The script | |
# uses grep (case-insensitive) to extract matching lines from the selected | |
# file, saving the output to: | |
# INPUTFILE_SEARCHPARAM.txt | |
# | |
# Comprehensive logging is done to both the terminal and the log file. | |
# | |
# Requirements: | |
# - fzf (for interactive file selection) | |
# | |
# Author: John | |
# Date: $(date '+%Y-%m-%d %H:%M:%S') | |
# ---------------------------------------------------------------------------- | |
# Exit immediately if any command fails, or if any variable is undefined. | |
set -euo pipefail | |
# Define log file with timestamp to avoid overwriting previous logs | |
TIMESTAMP=$(date '+%Y%m%d_%H%M%S') | |
LOGFILE="file_extract_${TIMESTAMP}.log" | |
# Number of lines to display in preview | |
PREVIEW_LINES=10 | |
# Make output directory if it doesn't exist | |
OUTDIR="extracted_results" | |
mkdir -p "$OUTDIR" | |
# ---------------------------------------------------------------------------- | |
# log: Logs messages with timestamps to both the console and LOGFILE. | |
# ---------------------------------------------------------------------------- | |
log() { | |
local level="$1" | |
shift | |
local message="$*" | |
echo "$(date '+%Y-%m-%d %H:%M:%S') - [${level}] $message" | tee -a "$LOGFILE" | |
} | |
# ---------------------------------------------------------------------------- | |
# spinner: Displays a simple spinner to indicate background progress. | |
# Parameters: | |
# $1 - The PID of the background process. | |
# ---------------------------------------------------------------------------- | |
spinner() { | |
local pid=$1 | |
local delay=0.1 | |
local spinstr='|/-\' | |
# Check if process is still running before starting spinner | |
if ! ps -p "$pid" > /dev/null; then | |
return 0 | |
fi | |
while ps -p "$pid" > /dev/null; do | |
for (( i=0; i<${#spinstr}; i++ )); do | |
printf "\r [%c] Processing..." "${spinstr:$i:1}" | |
sleep "$delay" | |
# Break inner loop if process ends | |
if ! ps -p "$pid" > /dev/null; then | |
break | |
fi | |
done | |
# Break outer loop if process ends | |
if ! ps -p "$pid" > /dev/null; then | |
break | |
fi | |
done | |
printf "\r Done! \n" | |
} | |
# ---------------------------------------------------------------------------- | |
# sanitize_filename: Sanitizes a string for safe use in filenames | |
# Parameters: | |
# $1 - The string to sanitize | |
# ---------------------------------------------------------------------------- | |
sanitize_filename() { | |
local input="$1" | |
# Replace problematic characters with underscores | |
echo "$input" | sed 's/[\/\\\:\*\?\"\<\>\|]/_/g' | |
} | |
# ---------------------------------------------------------------------------- | |
# Check for required commands | |
# ---------------------------------------------------------------------------- | |
check_requirements() { | |
log "INFO" "Checking requirements..." | |
if ! command -v fzf >/dev/null 2>&1; then | |
log "ERROR" "fzf is not installed. Please install fzf and re-run the script." | |
echo "You can install fzf with: sudo apt install fzf # For Debian/Ubuntu" | |
echo " or: brew install fzf # For macOS" | |
exit 1 | |
fi | |
log "INFO" "All requirements satisfied." | |
} | |
# ---------------------------------------------------------------------------- | |
# Main script execution | |
# ---------------------------------------------------------------------------- | |
main() { | |
log "INFO" "Script started" | |
# Check requirements | |
check_requirements | |
# ---------------------------------------------------------------------------- | |
# Step 1: Select a file using fzf from the current directory | |
# ---------------------------------------------------------------------------- | |
log "INFO" "Listing files in the current directory for selection." | |
# Use fzf with preview functionality | |
selected_file=$(find . -maxdepth 1 -type f -not -path "*/\.*" | sed 's|^\./||' | \ | |
fzf --prompt="Select a file: " \ | |
--preview="head -n $PREVIEW_LINES {}" \ | |
--preview-window=down:3:wrap) | |
if [ -z "$selected_file" ]; then | |
log "WARN" "No file was selected. Exiting." | |
exit 1 | |
fi | |
log "INFO" "File selected: $selected_file" | |
# Get file size for informational purposes | |
file_size=$(du -h "$selected_file" | cut -f1) | |
log "INFO" "Selected file size: $file_size" | |
# ---------------------------------------------------------------------------- | |
# Step 2: Get search parameters from the user | |
# ---------------------------------------------------------------------------- | |
echo -n "Enter search parameters (regex or text): " | |
read -r search_param | |
if [ -z "$search_param" ]; then | |
log "WARN" "No search parameters provided. Exiting." | |
exit 1 | |
fi | |
log "INFO" "Search parameters provided: $search_param" | |
# Ask user if they want case-sensitive search | |
echo -n "Case sensitive search? (y/N): " | |
read -r case_sensitive | |
grep_options="-i" # Default to case-insensitive | |
if [[ "${case_sensitive,,}" == "y" ]]; then | |
grep_options="" # Remove -i for case-sensitive search | |
log "INFO" "Using case-sensitive search" | |
else | |
log "INFO" "Using case-insensitive search" | |
fi | |
# ---------------------------------------------------------------------------- | |
# Step 3: Construct the output file name | |
# ---------------------------------------------------------------------------- | |
# Sanitize search parameter for use in filename | |
sanitized_param=$(sanitize_filename "$search_param") | |
# Truncate if too long (max 30 chars) | |
if [ ${#sanitized_param} -gt 30 ]; then | |
sanitized_param="${sanitized_param:0:30}" | |
fi | |
# Create a safe output filename | |
output_file="$OUTDIR/$(basename "$selected_file")_${sanitized_param}.txt" | |
log "INFO" "Output file set as: $output_file" | |
# ---------------------------------------------------------------------------- | |
# Step 4: Perform extraction using grep | |
# ---------------------------------------------------------------------------- | |
log "INFO" "Starting extraction using command: grep $grep_options \"$search_param\" \"$selected_file\" > \"$output_file\"" | |
# Start grep in background and capture any errors | |
grep $grep_options "$search_param" "$selected_file" > "$output_file" 2>"${output_file}.err" & | |
grep_pid=$! | |
# Show spinner while grep is running | |
spinner "$grep_pid" | |
# Wait for grep to finish and check exit status | |
if wait "$grep_pid"; then | |
# Success - grep found matches or completed successfully | |
grep_status=0 | |
else | |
# grep returned non-zero (could be no matches found, which is exit code 1) | |
grep_status=$? | |
fi | |
# ---------------------------------------------------------------------------- | |
# Step 5: Check and report extraction results | |
# ---------------------------------------------------------------------------- | |
# Check if there were errors | |
if [ -s "${output_file}.err" ]; then | |
log "ERROR" "Grep encountered errors: $(cat "${output_file}.err")" | |
rm -f "${output_file}.err" | |
exit 1 | |
else | |
rm -f "${output_file}.err" | |
fi | |
# Count matches | |
if [ -s "$output_file" ]; then | |
match_count=$(wc -l < "$output_file") | |
log "INFO" "Extraction successful. Found $match_count matching lines." | |
echo "===============================================" | |
echo " Extraction Results" | |
echo "===============================================" | |
echo " Source file: $selected_file ($file_size)" | |
echo " Search term: $search_param" | |
echo " Matches found: $match_count" | |
echo " Results saved to: $output_file" | |
echo "===============================================" | |
# Ask if user wants to preview results | |
echo -n "Preview results? (y/N): " | |
read -r preview | |
if [[ "${preview,,}" == "y" ]]; then | |
if command -v less >/dev/null 2>&1; then | |
less "$output_file" | |
else | |
head -n 20 "$output_file" | |
echo "..." | |
echo "[Showing first 20 lines only]" | |
fi | |
fi | |
else | |
if [ $grep_status -eq 1 ]; then | |
log "INFO" "No matching lines were found." | |
echo "No matching lines were found for '$search_param' in '$selected_file'." | |
else | |
log "ERROR" "Grep failed with status: $grep_status" | |
fi | |
fi | |
log "INFO" "Script finished successfully." | |
echo "All done! Check $output_file for your results." | |
} | |
# Run the main function | |
main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment