Bash

How can I store a command in a variable in a shell script

25 September 2026 · 5 min read

How can I store a command in a variable in a shell script

Shell scripting is a powerful skill for automating tasks and managing systems, but even seasoned developers sometimes grapple with seemingly simple challenges. One common question that arises is: how can I store a command in a variable in a shell script? This capability is fundamental for creating flexible, dynamic scripts that can adapt to different inputs or scenarios without constant manual modification. Understanding the nuances of command storage allows you to construct more robust and maintainable automation solutions, from simple alias-like shortcuts to complex, conditional execution flows. This guide will walk you through the various methods, from basic command substitution to advanced array usage, ensuring your scripts are both powerful and secure.

Understanding Command Substitution ($() or )

The most straightforward method to store the output of a command in a variable is through command substitution. This mechanism allows you to execute a command and then use its standard output as part of another command or assign it to a variable. Bash, like many modern shells, supports two forms: $(command) and the older, less recommended command (backticks).

When you use output=$(ls -l), the shell first executes ls -l. Whatever text ls -l prints to standard output is then captured and assigned as the value of the output variable. This is incredibly useful for capturing results, such as the current date, a list of files, or the output of a data processing pipeline. For instance, current_date=$(date +%Y-%m-%d) would store today’s date in a consistent format, ready for use in log filenames or reports.

While both $(…) and … achieve command substitution, $(…) is generally preferred. It handles nesting more gracefully (e.g., $(cat $(find . -name “.txt”))) and avoids the backslash escaping complexities often encountered with backticks. According to the GNU Bash Reference Manual, the $(command) form is the modern and recommended approach for clarity and robustness in shell scripting, helping to prevent common quoting and parsing errors. Always prioritize $(…) for new scripts to ensure better readability and maintainability.

Handling Arguments and Quoting Issues

Storing a command itself, rather than just its output, presents unique challenges, especially when arguments are involved. Simply assigning my_cmd=“ls -l” might seem intuitive, but executing $my_cmd later can lead to unexpected behaviors due to shell expansion processes like word splitting and globbing. When my_cmd is expanded, if it’s unquoted, the shell treats “ls”, “-”, and “l” as separate words, which is usually not what’s intended for a single argument like “-l”.

For example, if you have my_command=“echo hello world” and then run $my_command, it works as expected. But what if my_command=“ls -l .txt”? If there are no .txt files, the .txt might be passed literally to ls, or it might expand to all files if globbing rules are different. The critical issue arises when your command contains spaces or special characters that the shell interprets before execution. The shell performs various expansions on unquoted variables, including brace expansion, tilde expansion, parameter and variable expansion, command substitution, arithmetic expansion, word splitting, and pathname expansion (globbing). This sequence can drastically alter your intended command.

To mitigate these issues, it is paramount to always double-quote your variables when expanding them, especially those holding commands or their outputs. For instance, my_cmd=“ls -l” ; “$my_cmd” still treats “ls -l” as a single argument to be executed, which fails. This highlights that a simple string variable is often insufficient for storing a command and its arguments reliably. While eval “$my_cmd” can work, it introduces significant security risks and should be used with extreme caution, as it re-parses the string and executes it, potentially allowing arbitrary code injection if the variable’s content is untrusted.

Storing Commands with Arguments in Arrays

The most robust and recommended way to store a command along with its arguments in a shell script is by using Bash arrays. Arrays allow you to store each component of your command (the executable and each argument) as separate elements, preserving their integrity until execution. This approach bypasses the pitfalls of word splitting and globbing that plague simple string variables when dealing with complex commands.

To declare an array, you can use my_command=(ls -l /path/to/files), where ls, -l, and /path/to/files are stored as distinct elements. When you want to execute this command, you expand the array using “${my_command[@]}”. The double quotes around “${my_command[@]}” are crucial; they ensure that each element of the array is expanded into a separate word, preserving any spaces or special characters within individual arguments. This is vastly superior to “$my_command” for a string variable, which would expand the entire string as a single word.

Infographic here: A visual representation contrasting storing a command in a string variable vs. an array, highlighting word splitting issues vs. argument preservation.
Consider a scenario where you want to dynamically build a grep command. Using an array, you could do: grep\_options=(-r -i) ; search\_pattern="error" ; search\_path="/var/log" ; full\_command=(grep "${grep\_options\[@\]}" "$search\_pattern" "$search\_path"). Then, executing "${full\_command\[@\]}" would run grep -r -i error /var/log exactly as intended, regardless of spaces in the pattern or path. This method is particularly powerful for constructing commands where options or arguments might be conditionally added or removed, ensuring each component is passed correctly to the target executable.

Advanced Techniques and Best Practices

Question & Answer :
I would like to store a command to use at a later time in a variable (not the output of the command, but the command itself).

I have a simple script as follows:

command="ls"; echo "Command: $command"; #Output is: Command: ls b=`$command`; echo $b; #Output is: public_html REV test... (command worked successfully) 

However, when I try something a bit more complicated, it fails. For example, if I make

command="ls | grep -c '^'"; 

The output is:

Command: ls | grep -c '^' ls: cannot access |: No such file or directory ls: cannot access grep: No such file or directory ls: cannot access '^': No such file or directory 

How could I store such a command (with pipes/multiple commands) in a variable for later use?

Use eval:

x="ls | wc" eval "$x" y=$(eval "$x") echo "$y"