Special Parameters in Bash
Have you ever wondered how to get Process ID of the last background process? Or how to print PID of the current shell? Today, we’ll cover special parameters in Bash that can help you work with scripts more effectively.
What are special parameters?
First of all what are special parameters in bash?
These are predefined variables that provide information about script’s execution environment, process management and arguments. These parameters are denoted with special symbols (like $$
, $!
, $?
) and have unique purposes making them essential for scripting.
The best way to learn bash is to work with live examples. In this part I will try to present the most useful special parameters with a description and, of course, examples so that you can test for yourself how it works.
$0
The first useful expansion parameter is $0
, which represents the name of the script or shell currently being executed. Create a file for example print_name.sh
and paste following snipped into this file via your favourite text editor to verify that.
$ touch print_name.sh
$ vi print_name.sh
# print_name.sh
#!/bin/bash
echo $0
Don’t forget to make script executable.
$ chmod +x print_name.sh
We can now verify usage of $0
.
$ ./print_name.sh
./print_name.sh
$1, $2, $3 …
$1, $2, $3 ...
are positional parameters that represent the arguments passed to script or function. Each number corresponds to an argument based on its order.
Let’s modify our print_name.sh
and add following lines to the end.
echo $1
echo $2
# print_name.sh
#!/bin/bash
echo $0
echo $1
echo $2
We can pass now arguments to our script and print them. Let’s run our script again and see the output.
$ ./print_name.sh arg1 arg2
./print_name.sh
arg1
arg2
$#
$#
represents total number of positional parameters passed to our script or function. It is commonly used to check how many arguments did user provide to script.
Let’s add echo $#
command to the end of our print_names.sh
script.
# print_name.sh
#!/bin/bash
echo $0
echo $1
echo $2
echo $#
If we pass 3 arguments to our script number 3
should be printed. Let’s verify that.
$ ./print_name.sh arg1 arg2 arg3
./print_name.sh
arg1
arg2
3
$@ and $*
$@
- represents all positional parameters passed to a script or function, treating each argument as a separate entity.
$*
- represents all positional parameters passed to a script or function as single, combined string. By default it joins arguments using the first character of the IFS (Internal Field Separator), which is usually a space. We will cover example to show how it behaves with custom IFS later.
For these parameters we will use another example. Let’s create file named all_parameters.sh
. We will use simple for loops to iterate over passed argument and check how $@
differs from $*
. Note that this example uses default IFS. Which can be checked in a human-readable way by following command.
$ printf '%q\n' "$IFS"
$' \t\n'
We can see that our default IFS consists of space, tab and new line.
Let’s go back to our example.
$ touch all_parameters.sh
$ vi all_parameters.sh
# all_parameters.sh
#!/bin/bash
echo "Using \$@:"
for arg in "$@"; do
echo "$arg"
done
echo "Using \$*:"
for arg in "$*"; do
echo "$arg"
done
Again - don’t forget to make script executable.
$ chmod +x all_parameters.sh
Let’s check output of our all_parameters.sh
script, by providing some arguments.
$ ./all_parameters.sh arg1 arg2 with spaces arg3
Using $@:
arg1
arg2
with
spaces
arg3
Using $*:
arg1 arg2 with spaces arg3
As we can see, $@
preserves each argument as a separate string, while $*
combines all arguments into a single string, separated by the first character of IFS, which may not always be ideal if spaces are involved and we use default IFS.
But how does it act when we set custom IFS? Let’s check it. We will modify our all_parameters.sh
script by providing custom IFS at the top.
# all_parameters.sh
#!/bin/bash
IFS=','
echo "Using \$@:"
for arg in "$@"; do
echo "$arg"
done
echo "Using \$*:"
for arg in "$*"; do
echo "$arg"
done
Using the same arguments we get following output.
$ ./all_parameters.sh arg1 arg2 with spaces arg3
Using $@:
arg1
arg2
with
spaces
arg3
Using $*:
arg1,arg2,with,spaces,arg3
Now we can see that by using $*
arguments are joined by ,
, which might be more useful than space.
$$ and $!
$$
represents the PID (Process ID) of the current shell or script that is running.
$ echo $$
61730
It can be used for various purposes, for example we can create a script that will create temporary file and ensure the file name will not collide with other names if this script was run in parallel. Let’s create script uniq_name.sh
and make it executable.
$ touch uniq_name.sh
$ chmod +x uniq_name.sh
$ vi uniq_name.sh
# uniq_name.sh
#!/bin/bash
sleep 2
temp_file="./tempfile_$$.txt"
echo "Creating temporary file: $temp_file"
echo "Temporary data" > "$temp_file"
cat "$temp_file"
We are going to run 3 instances of that script in parallel to see if it works.
for i in {1..3}; do ./uniq_name.sh & done
Creating temporary file: ./tempfile_62561.txt
Temporary data
Creating temporary file: ./tempfile_62563.txt
Temporary data
Creating temporary file: ./tempfile_62562.txt
Temporary data
As we can see names of the files are unique.
$!
on the other hand represents PID of the last command run in the background. When starting command in the background by appending & to it $!
holds the PID of that background process.
It can be checked by executing following commands. By using $!
we can wait for background command to finish its execution.
sleep 5 &
background_pid=$!
echo "Waiting for job with PID $background_pid to finish..."
wait $background_pid
echo "Job completed."
$?
$?
hold the exit status of the last command that was executed. By using $?
it can be verified if previous command ran successfully or encountered an error.
If $?
returns 0, it indicates that the last command succeeded; any other value means that the command encountered an error.
Let’s create a simple script status.sh
which will check if previous command succeeded or not.
$ touch status.sh
$ chmod +x status.sh
$ vi status.sh
We will compare exit status to 0
using [ $? -eq 0 ]
test functionality.
# status.sh
#!/bin/bash
filename=$1
ls "$filename"
if [ $? -eq 0 ]; then
echo "File exists!"
else
echo "File doesn't exist."
fi
Now lets run our script and provide as an argument filename that doesn’t exist.
$ ./status.sh test
ls: cannot access 'test': No such file or directory
File doesn't exist.
As we can see ls command returned non-zero output. Now let’s create file test
and see what happens.
$ > test
$ ./status.sh test
test
File exists!
Summary
Bash special parameters, like $0
, $#
, $@,
$?
, and $!
, provide powerful tools for script control and flexibility. Understanding these parameters allows you to access command-line arguments, track process IDs, and manage command outcomes, making it easier to handle complex scripting tasks. By mastering these parameters, you can write more robust and dynamic scripts, automate tasks with precision, and improve error handling. Experimenting with these parameters in your own scripts will deepen your understanding and enhance your efficiency as a Bash user.