As a Linux system administrator, understanding Bash scripting is crucial for automating repetitive tasks and improving workflow. One of the most useful yet perplexing aspects of Bash scripting is the special $_ variable.

What is the $_ variable?

The $ variable refers to the last argument of the previously executed command. This allows you to reuse the last argument easily without having to retype it. Some key things to know about $:

  • It contains the last argument from the previously executed command
  • It can be used to chain commands together and reuse arguments
  • It only applies to the immediately preceding command

Understanding the nuances of $ will allow you to write cleaner and more effective Bash scripts. In this comprehensive guide, we‘ll cover common use cases, pitfalls to avoid, and plenty of examples to help demystify $.

Common Use Cases

Here are some of the most popular ways that Linux admins utilize the $_ variable:

1. Chaining Commands

One of the most common uses of $_ is chaining commands together:

$ ls *.txt 
file1.txt file2.txt
$ cat $_
# Contents of file2.txt

Since ls *.txt was executed last, $_ contains the last argument from that command (file2.txt). We can then directly pipe that into cat without retyping the filename.

This makes it easy to reuse the last argument for your next operation.

2. Debugging Scripts

During Bash script development, $_ can be handy for debugging:

#!/bin/bash

input="/home/user/documents"
echo "Input directory is $input"

# Do something with $input

echo "Last argument was $_"

This allows you to easily verify what $input contained last without having to echo $input again.

3. Validating Input

$ can also be used to validate input in Bash scripts. Let‘s say you prompt a user to enter a filename, you can check if it exists using $ like this:

read -p "Enter filename: " f

if [ -f $_ ]; then
  echo "$_ exists."
else 
  echo "$_ does not exist!"
  exit 1
fi

Again this saves you from having to store their input in another variable just to check if the file exists.

4. repeats the last token of a shell input line in a prompt

$_ can be used in an interactive bash prompt to repeat the last token of the previos command. This saves the user from having to retype a long trailing argument.

For example:

$ /usr/bin/find /home -name *.log
$ vim $_

This will open the last file matching *.log in vim, without having to type the full filename again.

The PS1 prompt can be configured to do this automatically:

PS1=‘$_ $ ‘

Now the last token will be inserted into every prompt, ready to reuse!

Caveats and Pitfalls

While $_ can be useful, there are some critical things to know before relying on it in scripts:

  1. It only applies to the immediately preceding command. If you execute any other command after the one you want, $_ will change:
$ ls -l /etc > /tmp/list
$ cd /home/user 
$ echo $_ 
# => /home/user

Here $_ reflects cd not ls because it was the last command run.

  1. Commands that don‘t have arguments will cause issues. $_ will expand to nothing:
$ date
$ echo $_ 
# => (no output)
  1. unclosed quotations could lead to unexpected output. Consider this example:
$ echo "Hello world
$ grep o $_
# => Hello world
# 
# => (shows content of all files in pwd)   

Since the first echo has unclosed quotes, $_ stores that literal string. When passed to grep, this actually searches all files in the pwd instead of just that string!

Always double check that $_ contains the expected output before using it.

Tips and Best Practices

Follow these tips when working with $_:

  • Be defensive – Never assume $_ contains what you expect.
  • Explicitly check that commands have successfully exited before depending on $_.
  • Use $_ to reuse arguments immediately after commands rather than in arbitrary later commands.
  • Prefer storing important arguments in named variables rather than depending on $_.
  • Use curly braces {} around $_ if it is followed by other text to avoid issues.

Real-World Examples

With some background on $_ now covered, let‘s walk through some real-world examples in action.

We‘ll see various use cases like chaining commands, integrating with loops, streamlining interactive prompts, and more.

Interactive Usage

Basic interactive use cases involve reusing file/directory arguments for multiple commands:

# Copy contents of install log to install_log.old
$ cp /var/log/install.log /var/log/install_log.old  

# Print 5 last lines of install log to verify changes       
$ tail -5 $_          

Here we reuse the "install.log" argument rather than typing the full filename out again just to pass it to tail.

Or suppose you want to extract a tar.gz file from /tmp, list its contents, and then delete it:

# Extract content
$ tar xzf /tmp/package.tar.gz  

# List contents  
$ ls $_ | grep -i bin   

# Remove extracted directory
$ rm -rf $_  

This allows you to act on that extracted directory without having to explicitly name it or even know it ahead of time.

Integrating with Loops

A common task is iterating files or directories with a loop construct.

We can integrate $_ to minimize repeated code:

# Generate tmp files
for i in {1..5}; do
  touch /tmp/file$i
  echo "Temporary file #$i" > $_
done

# Now loop through and print them  
for i in {1..5}; do
  echo "======= Contents of file #$i ======="; 
  cat /tmp/file$i
done

Rather than specifying the full /tmp/file$i filename on both loops, we reuse $_ on the second loop to retrieve the last file from the first loop.

Streamlining User Prompts

Bash scripts often need to prompt users for input like filenames. We can use $_ here too:

#!/bin/bash

read -p "Enter filename: " f

echo -n "Does $_ exist? "
if [ -f $_ ]; then
  echo "Yes"
else
  echo "No such file"
fi

This validates if their entered file exists without needing a separate $f variable.

We can make things even smoother by using $_ directly in the prompt:

#!/bin/bash

read -p "Enter filename: " f
echo $_  

read -p "$_ exists. Open in vim? " ans

if [[ $ans = [yY] ]]; then
  vim $_
fi

Now the entered filename is displayed back to them for confirmation before opening in vim.

One-Liners

The concise nature of $_ also lends itself well to Bash one-liners – compact scripts to accomplish basic tasks quickly without writing a formal script.

A few examples:

# Print file permissions for all *.conf files
$ ls -l *.conf | awk ‘{print $1}‘ | xargs -I{} echo {} $_ 

# Calculate total disk usage for music dirs 
$ du -sh ~/music/* | awk ‘{sum+=$1} END {print sum}‘

# Rename all JPEGs in pwd to .jpg extension
$ rename ‘s/\.JPEG$/.jpg/‘ *.JPEG  

These demonstrate how $_ can cleanly chain together longer pipelines without unnecessarily complicating variable usage or command organization.

In Closing

The $_ variable may seem mystifying initially, but as shown here, it has abundant usefulness in shell scripting – from chaining commands to integrating loops and streamlining prompts. Mastering its nuances takes time but pays dividends in writing cleaner and more maintainable Bash scripts.

The key is understanding exactly when $ gets populated, how it interacts with quotations, and when to leverage it over user-defined variables. Used judiciously, $ introduces functionality similar to other scripting languages directly in the shell environment. Combine it with other Bash features, and you have an incredibly powerful scripting toolkit at your fingertips without relying on external dependencies.

Similar Posts