如何使用Bash脚本查找与指定人员距离最近的人员姓名及对应距离
Problem Context
I need to write a Bash script proj01.sh that calculates the closest 3D distance from a specified person (via index) to others in a CSV file, then outputs the closest person's name and distance. Here's the full breakdown of requirements and where I'm stuck:
Files & Input/Output Details
Sample Input File (sampleInputFile)
Name, Xloc, YLoc, Zloc John, 10.5, 80.1, 5.80 Mary, 30.4, 20.5, 9.20 Paul, 10, 1000.0, 10.6
Expected Usage & Output
Input command:
proj01.sh sampleInputFile 1
Output (tocorrespondingOutputFileand console):Mary 62.9(or rounded to63.0)
Core Requirements
- Validate that the input file exists; exit with an error message if not.
- Validate the person index is a positive integer within the range of available entries.
- Calculate 3D distance using the formula:
d = sqrt( (x1-x2)² + (y1-y2)² + (z1-z2)² )
My Current Progress
I can read the file's rows, but I'm unsure how to extract the target person's coordinates, compare them to all others, and find the minimum distance. Here's my code snippet so far:
{ read while IFS=, read -r n x y z do echo "n: $n x: $x y: $y z: $z" done } < $inputFile
Complete Solution Script
Here's a full implementation of proj01.sh that covers all your requirements, with explanations for key parts:
#!/bin/bash # Check if we have exactly 2 arguments if [ $# -ne 2 ]; then echo "Usage: $0 <input-file> <person-index>" exit 1 fi input_file="$1" target_idx="$2" # Validate input file exists if [ ! -f "$input_file" ]; then echo "Error: File '$input_file' does not exist." exit 1 fi # Validate index is a positive integer if ! [[ "$target_idx" =~ ^[0-9]+$ ]]; then echo "Error: Person index must be a positive integer." exit 1 fi # Count total number of people (skip header row) total_people=$(tail -n +2 "$input_file" | wc -l) # Validate index is within valid range if [ "$target_idx" -lt 1 ] || [ "$target_idx" -gt "$total_people" ]; then echo "Error: Person index must be between 1 and $total_people." exit 1 fi # Extract target person's name and coordinates # Skip header, grab the target row, clean up commas/whitespace read -r target_name target_x target_y target_z <<< "$(tail -n +2 "$input_file" | sed -n "${target_idx}p" | tr ',' ' ' | xargs)" # Initialize variables for tracking minimum distance min_dist_sq=999999999 # Using squared distance to avoid sqrt until final step closest_name="" # Loop through all people to calculate distances { read # Skip header row current_line=1 while IFS=, read -r name x y z; do # Skip the target person themselves if [ "$current_line" -eq "$target_idx" ]; then current_line=$((current_line + 1)) continue fi # Clean up whitespace from coordinates x=$(echo "$x" | xargs) y=$(echo "$y" | xargs) z=$(echo "$z" | xargs) # Calculate squared differences (using bc for floating point math) dx=$(echo "$target_x - $x" | bc) dx_sq=$(echo "$dx * $dx" | bc) dy=$(echo "$target_y - $y" | bc) dy_sq=$(echo "$dy * $dy" | bc) dz=$(echo "$target_z - $z" | bc) dz_sq=$(echo "$dz * $dz" | bc) # Sum squared differences (squared distance) dist_sq=$(echo "$dx_sq + $dy_sq + $dz_sq" | bc) # Update minimum distance if current is smaller if (( $(echo "$dist_sq < $min_dist_sq" | bc -l) )); then min_dist_sq="$dist_sq" closest_name="$name" fi current_line=$((current_line + 1)) done } < "$input_file" # Calculate final distance (sqrt of squared distance) with 1 decimal place final_dist=$(echo "scale=1; sqrt($min_dist_sq)" | bc) # Output to both console and correspondingOutputFile echo "$closest_name $final_dist" | tee correspondingOutputFile
Key Explanations
- Input Validation:
- We first check argument count, file existence, index format, and index range to avoid runtime errors.
- Target Data Extraction:
tail -n +2skips the header,sed -n "${target_idx}p"grabs the specific row, andtr/xargscleans up commas and extra whitespace to make variable assignment easy.
- Distance Calculation:
- We use squared distance instead of actual distance for comparisons (since sqrt is a monotonic function, the smallest squared distance corresponds to the smallest actual distance) – this saves computation time.
bcis used for floating-point arithmetic, which Bash doesn't handle natively.
- Result Output:
teewrites the result to both the console andcorrespondingOutputFileas required.
Test It Out
Run the script with your sample input:
chmod +x proj01.sh ./proj01.sh sampleInputFile 1
You'll get the output Mary 62.9 (and the same line in correspondingOutputFile).
内容的提问来源于stack exchange,提问作者Malapati Chetan

