DEV Community

Cover image for 1: The Problems/Errors I Faced While Writing BashScripts And The Solutions also
SAHIL
SAHIL

Posted on

1: The Problems/Errors I Faced While Writing BashScripts And The Solutions also

1. Automated Logging with exec

Logging every line of a script manually is tedious. Using exec redirects the entire script's output stream.

The Problem Without It

  • The Error: Without global redirection, you have to append >> logfile.txt 2>&1 to every single command. If you forget one, that output is lost to the terminal and never recorded.
  • The "Read" Conflict: Once you redirect stdin (standard input), the read command starts looking at your log file or pipe for input instead of your keyboard. The script will either hang or crash because it can't find the "input" it needs.

The Solution

exec > >(tee -a "script.log") 2>&1

Enter fullscreen mode Exit fullscreen mode
  • How it works: It uses Process Substitution. It sends stdout (1) and stderr (2) into a pipe that tee reads. tee then writes to the file and back to the terminal.
  • The Thought Process: To fix the read issue, you must explicitly tell the script to look at the physical terminal for input:
read -p "Enter Name: " username < /dev/tty

Enter fullscreen mode Exit fullscreen mode

Why it helps: It ensures 100% of errors are caught in the log without sacrificing the ability to have an interactive UI.

2. Processing Files: while read vs. for loop

The Problem Without It

  • The Error: Using for line in $(cat file.txt) breaks if a line contains a space. The for loop treats spaces as delimiters, so a line like "John Doe" becomes two separate iterations.
  • The Thought Process: We need a tool that respects the Newline character \n specifically.

The Solution

while IFS= read -r line; do
    echo "Processing: $line"
done < file.txt

Enter fullscreen mode Exit fullscreen mode

How it helps:

while read processes the file line-by-line. The -r flag prevents backslashes from being interpreted as escape characters, preserving the data exactly as it exists in the file.

3. Non-Interactive Password Updates

The Problem Without It

  • The Error: The standard passwd command is interactive. If you run it in a script, it pauses and waits for a human to type the password twice. This hangs an automated deployment script.
  • The Thought Process: We need a way to "pipe" the credentials directly into the system's authentication database.

The Solution

echo "$username:$password" | chpasswd
Enter fullscreen mode Exit fullscreen mode
  • How it helps: chpasswd is designed specifically for batch processing. It accepts user:pass strings from stdin, making it perfect for loops and automation.

4. Clean Archiving

The Error: If you run

tar -cvf backup.tar /home/user/data/file.txt
Enter fullscreen mode Exit fullscreen mode

when you extract it, tar will recreate the entire folder structure /home/user/data/.

The Solution:

tar -C /home/user/data/ -cvf backup.tar file.txt
Enter fullscreen mode Exit fullscreen mode

How it helps: The -C (change directory) flag tells tar to "step into" that folder before arching, so the metadata only contains the filename.

Using basename

  • The Thought Process: If you have a full path like /var/logs/app/error.log, but you only want the string error.log for a backup name.
  • The Command: FILE_NAME=$(basename "/var/logs/app/error.log").
  • How it helps: It strips the directory path, allowing you to create clean, dynamic labels for your backups.

5. Reliability over $?

The Problem with $?
The Error: $? (the exit status) only captures the result of the last command executed.

The Risk: If you call a function that checks a service, but the function has a small echo or cleanup command at the very end, $? will return the status of the echo, not the service check. You might think the service is running when it actually failed.

The Solution: Direct Boolean Logic
Instead of checking the number, use the command directly in an if statement.

Avoid: systemctl is-active service; status=$?; if [ $status -eq 0 ]...

Better: if systemctl is-active --quiet service; then ...

  • How it works: In Bash, an if statement evaluates the exit code of the command following it.
  • Why it helps: It eliminates the "middleman" variable. It is cleaner, less prone to being overwritten by an accidental command in between, and more readable.

6. Short-Circuiting

In Bash, && represents AND. For an "AND" statement to be true, both sides must be true. If the first part (the condition) fails, Bash doesn't even bother looking at the second part—it just stops.

The Problem with the if Block
The standard if statement is visually heavy for simple tasks:

if [[ $USER == "root" ]]; then
  echo "Access granted."
else
  echo "Access denied."
  exit 1
fi
Enter fullscreen mode Exit fullscreen mode

The Issue: It takes 6 lines to perform one simple check. In a long script, this creates "visual noise" that makes it harder to spot the actual logic.

The Solution: Grouped Commands

To run multiple commands (like an echo followed by an exit) on a single line, you must group them.

Option A: Using Parentheses ( )
This runs the commands in a subshell.

[[ $USER != "root" ]] && (echo "Error: Must be root"; exit 1)
Enter fullscreen mode Exit fullscreen mode

The Catch: Because it’s a subshell, the exit command will exit the subshell, not your main script. Avoid this if you actually want the script to stop.

Option B: Using Curly Braces { } (Recommended)
This runs the commands in the current shell context.

[[ $USER != "root" ]] && { echo "Error: Must be root"; exit 1; }
Enter fullscreen mode Exit fullscreen mode

Crucial Syntax:

  • There must be a space after the opening {.
  • Each command inside must end with a semicolon ;.
  • There must be a space before the closing }.

Handling the "Else" (The Ternary Style)

If you want to emulate an if/else on one line, you can chain && and ||.


[[ -f "config.txt" ]] && echo "Found it" || { echo "Missing file"; exit 1; }

Enter fullscreen mode Exit fullscreen mode

The "Thought Process" & Common Errors

  • The "Unexpected Exit" Error: If you write [[ condition ]] && echo "Success" || exit 1, and for some reason the echo fails (e.g., the disk is full or stdout is closed), the exit 1 will trigger even if the condition was true.
  • The Fix: Using the grouped { } after the && ensures that the "Success" logic and the "Failure" logic stay strictly separated.

So that wraps up the errors I faced and concepts I used to troubleshoot them. All the scripts are currently in my Github account
(https://github.com/sahil0907/BashScripting)

Feel free to share your insights and if there are any inputs I would love to hear over my LinkedIn
(https://www.linkedin.com/in/sahil0907/).

Have a nice day and Keep Learning.

Top comments (0)