从被调用Bash脚本为父脚本变量赋值的方案与最佳实践
First, let's address your core constraints: your inner script uses the alternate screen buffer for user input (so stdout is tied up with the menu), you have more than 256 options (making exit codes useless), and you want to avoid the pitfalls of source + eval. Here are the most robust solutions tailored to your needs:
Solution 1: Use a Temporary File (Simple & Reliable)
This is the easiest approach to implement without messing with file descriptors. The inner script writes its result to a temp file, and the parent reads it after the inner script exits.
Parent Script (parent.sh)
#!/bin/bash # Create a unique temporary file to avoid conflicts temp_file=$(mktemp) # Run the inner script, passing the temp file path as an argument bash inner.sh "$temp_file" # Read the result from the temp file into your variable result=$(cat "$temp_file") # Clean up the temp file to avoid clutter rm "$temp_file" # Use the captured result echo "Selected value: $result"
Inner Script (inner.sh)
#!/bin/bash temp_file="$1" # Your menu logic here (uses alternate screen buffer for UI) # Example: Simulate selecting an option with a value beyond 256 selected_value="advanced_option_350" # Write the result to the temp file (no conflict with stdout menu output) echo "$selected_value" > "$temp_file"
Pros:
- No conflicts with stdout/stderr (result is isolated to a file)
- Supports any result type (strings, large numbers, values with special characters)
- No scope pollution (unlike
source) - Simple to understand and maintain
Cons:
- Requires creating and cleaning up a temp file (but
mktemphandles uniqueness safely)
Solution 2: Use a Custom File Descriptor (No Temp Files)
If you want to avoid disk I/O, redirect the result to a custom file descriptor that the parent captures. This keeps all data in memory.
Parent Script (parent.sh)
#!/bin/bash # Capture fd 3 from the inner script into the result variable result=$(bash inner.sh 3>&1 1>&2) # Use the captured result echo "Selected value: $result"
Inner Script (inner.sh)
#!/bin/bash # Your menu logic here (writes to stdout, which is redirected to parent's stderr) echo -e "\e[?1049h" # Enter alternate screen buffer echo "Select an option:" echo "1. Basic Option" echo "350. Advanced Option" echo -e "\e[?1049l" # Exit alternate screen buffer # Write the result to fd 3 (captured by parent's stdout) echo "advanced_option_350" >&3
How it works:
3>&1redirects the inner script's fd 3 to the parent's stdout (which we capture with$()).1>&2redirects the inner script's stdout (menu UI) to the parent's stderr, so it doesn't interfere with the result.
Pros:
- No temp files needed (in-memory transfer)
- Fast and clean
- Supports any result type
Cons:
- Requires careful file descriptor management (easy to mix up if you're new to Bash redirection)
- Menu output goes to stderr (acceptable unless you already use stderr for error messages—adjust to another fd if needed)
Solution 3: Pass Parent Variable Name (Safe Eval Alternative)
If you want the inner script to directly set the parent's variable (like your source+eval idea but safer), pass the variable name to the inner script and use printf %q to safely escape values before returning an assignment command.
Parent Script (parent.sh)
#!/bin/bash # Capture the safe assignment command from the inner script assignment=$(bash inner.sh "result") # Evaluate the assignment safely (no injection risks thanks to %q) eval "$assignment" # Use the captured result echo "Selected value: $result"
Inner Script (inner.sh)
#!/bin/bash var_name="$1" # Your menu logic here selected_value="option with spaces & special chars!" # Print a safely escaped assignment command printf "%s=%q\n" "$var_name" "$selected_value"
How it works:
- The inner script outputs a string like
result=option\ with\ spaces\ \&\ special\ chars\!, which is properly escaped for Bash. - The parent evaluates this string with
eval, but the%qflag ensures special characters are handled safely, eliminating injection risks.
Pros:
- Directly sets the parent's variable as requested
- Safe from eval-related security issues
- No temp files or fd juggling
Cons:
- Still uses
eval(though in a controlled, safe way) - Requires redirecting menu output to avoid conflicting with the assignment command (like in Solution 2)
Best Practices for Your Scenario
- Prefer Temp Files or Custom FDs: If you want to avoid
evalentirely, these are the safest, most straightforward options. - Isolate Menu Output: Always redirect menu UI output to a separate channel (stderr or a custom fd) to prevent conflicts with result data.
- Use
mktempfor Temp Files: Never hardcode temp file paths—mktempcreates unique files to avoid race conditions. - Escape Special Characters: If your result values can have spaces, quotes, or special characters, use
printf %q(Solution 3) or ensure temp file content is read correctly (usingcatworks for multi-line values). - Avoid Exit Codes: As you noted, exit codes are limited to 0-255, so they're unsuitable for your large option set.
内容的提问来源于stack exchange,提问作者tifrel

