LearnRHCSA (EX200)Shell Scripts

Capturing Command Output

bash - a hands-on Linux lab on a real virtual machine.

Run a command and keep its output as a value with command substitution. Capture into a variable with $(command), splice inline, do integer math with $(( )), nest a pipeline inside one $( ), and feed the result into a message, a loop, or a test. Serves the EX200 shell-scripts move of building a value from a command, such as a date for a timestamped backup filename.

The exam hands you a scripting task: write a backup script that names its archive after today's date. You know how to run date. But you cannot copy the date out of one command and paste it into a filename by hand, because the script has to do that itself, every day, unattended. The value has to come FROM a command and land INSIDE your script.

That move, run a command and keep its output as a value you can use, is one of the most reused patterns in shell scripting. It builds filenames, feeds loops, and decides if tests. It is called command substitution. By the end of this lesson you will capture a command's output into a variable and use it in a message, a loop, a test, and a piece of arithmetic.

The black boxes below are a practice terminal: a safe sandbox that checks the one command each step teaches. The outputs shown are a real capture from a RHEL 10 machine (AlmaLinux 10.2). Some values, like how many accounts use bash or how many files sit in a folder, depend on the machine, so your own numbers can differ. The SHAPE and the moves are what carry to the exam.

Command substitution runs a command and replaces itself with that command's output. You wrap the command in $( and ), and the shell hands you back whatever the command printed, as text you can store or drop into a line.

The shape is $(command). Assign it to a variable with NAME=$(command) and the variable now holds the output. There is an older form using backticks, ` command , that does the same job, but you should reach for $( ) every time. It reads more clearly, and it nests cleanly: you can put one $( ) inside another. Backticks cannot nest without ugly escaping, which is why $( )` is the exam-safe choice.

A script that cannot capture output is stuck with values you hard-code by hand. The moment a task says name the file after the date, count the matching lines, or act only if this command reports success, you need the command's answer as a value. Command substitution is how a script reads the machine and reacts, instead of running blind.

Start with the core move. You want to know how many accounts on this machine use the Bash login shell. grep -c '/bin/bash

#39; /etc/passwd prints that count. Capture it into a variable named count, then print a sentence that includes the captured number.

The $(...) runs the grep and stores its output in count. Then echo prints a message with $count spliced in:

count=$(grep -c '/bin/bash

#39; /etc/passwd); echo "bash shells in passwd: $count"

prompt: student@servera:~$ answer: count=$(grep -c '/bin/bash

#39; /etc/passwd); echo "bash shells in passwd: $count" output: bash shells in passwd: 2 hint: Assign with NAME=$(command), no spaces around the =, then echo a double-quoted message with $count inside.

The grep -c printed 2, and the $(...) captured that 2 into count instead of letting it hit the screen. Then echo printed your sentence with the value dropped in where you wrote $count. That is the whole pattern: a command's output becomes a variable, and the variable becomes part of a message. Notice the assignment has NO spaces around the =. count = $(...) would break, because the shell would read count as a command.

The number 2 is this image's account list. A machine with more interactive users would report a higher count. What never changes is the mechanic: NAME=$(command) stores the output, and $NAME reuses it.

You do not always need to park the output in a variable first. You can drop $(...) directly inside a larger line, and the shell swaps in the output before running the rest. This is the fast form for one-off values.

Here you count how many .txt files are in the current directory and say it in one breath. ls *.txt lists them, wc -l counts the lines, and the whole count substitutes right into the sentence:

echo "there are $(ls *.txt | wc -l) text files here"

prompt: student@servera:~$ answer: echo "there are $(ls *.txt | wc -l) text files here" output: there are 2 text files here hint: Put the whole counting command inside $( ) right where the number belongs in the double-quoted string.

The shell ran ls *.txt | wc -l first, got 2, substituted that into the string, and only then ran echo. So you read one finished sentence. This inline form is perfect when you use a value exactly once. When you need the same value more than once, capture it into a variable instead, so the command runs a single time.

The 2 is the count of text files in this demo folder. Another directory holds a different number. The point is the placement: $(...) can live anywhere inside a double-quoted line, and the shell fills it in before running the line.

Command substitution captures TEXT. When you need the shell to actually calculate, you reach for its sibling, arithmetic expansion, written $(( )). Two parentheses, not one. Inside, the shell does integer math and hands back the result, which you can print or capture just like a substitution.

Multiply seven by six and print the result inside a labelled line:

echo "7 * 6 = $((7 * 6))"

prompt: student@servera:~$ answer: echo "7 * 6 = $((7 * 6))" output: 7 * 6 = 42 hint: Arithmetic uses double parentheses: $(( ... )). Put the sum inside and echo it in a double-quoted string.

The $(( 7 * 6 )) evaluated to 42, and that number landed in your sentence. Keep the two apart in your head: $( ) runs a command and keeps its output as text, while $(( )) does integer arithmetic. You will often combine them, capture two numbers with $( ), then add them with $(( )). Note that $(( )) is integer only; it has no fractions, so $((7 / 2)) gives 3, not 3.5.

The real power of $( ) shows up when you feed one capture into another. Because $( ) nests without escaping, you can build a value in stages. Here you grab the very first line of /etc/passwd with head -1, pull its first colon-separated field with cut -d: -f1, and report which account that is.

The inner $(head -1 /etc/passwd) runs first and produces the whole root line; the outer command then cuts the username out of it:

echo "first account is $(head -1 /etc/passwd | cut -d: -f1)"

prompt: student@servera:~$ answer: echo "first account is $(head -1 /etc/passwd | cut -d: -f1)" output: first account is root hint: Inside the $( ), pipe head -1 into cut -d: -f1 to pull the first field of the first line.

The pipeline inside $( ) did the work: head -1 took the first line of /etc/passwd, cut -d: -f1 sliced off the field before the first colon, and the result, root, substituted into your sentence. This is why $( ) beats backticks: you could nest a second $( ) inside this one and the shell would still read it cleanly. On the exam, when a value takes two or three commands to build, you build it in one $( ) like this.

The first line of /etc/passwd is root on essentially every RHEL system, so this output is stable. The technique, not the name, is the lesson: chain commands inside one $( ) to build exactly the value you need.

A captured value is just text, so it fits anywhere the shell reads text. Two exam-critical spots are loops and if tests.

Feed a capture into a for loop to iterate over what a command produced. for u in $(cut -d: -f1 /etc/passwd); do echo "user=$u"; done walks every username in the file. And use a capture inside a test to decide a branch: if [ "$(whoami)" = "root" ]; then echo root; fi runs the command, compares its output, and acts on it.

Always wrap a capture in double quotes inside a test: write if [ "$(whoami)" = "root" ], not if [ $(whoami) = root ]. If the command ever returns an empty string or a value with a space, the unquoted form breaks the [ ] test with a confusing error. The single-bracket [ ] test is the portable form the exam expects; bash also offers [[ ]], which is more forgiving, but [ ] is the safe default to reach for.

Scaffolding off. No command is shown this time. You have every piece you need.

This is the backup-filename task from the top of the lesson. Capture today's date in YYYYMMDD form using date +%Y%m%d, store it in a variable called today, and then print the filename backup- followed by that date and .tar.gz. Think about the assignment form with no spaces around =, and how ${today} splices the value in when it sits right next to other text.

prompt: student@servera:~$ answer: today=$(date +%Y%m%d); echo "backup-${today}.tar.gz" output: hint: Assign today=$(date +%Y%m%d) with no spaces around the =, then echo "backup-${today}.tar.gz". The braces mark where the variable ends.

You captured the date once into today, then reused it to build the filename. The ${today} braces matter here: they tell the shell exactly where the variable name ends, so backup-${today}.tar.gz reads the value and not a variable called today.tar. No output is pinned because the date changes every day, but the shape is the exam answer: capture a command's output into a variable, then splice it into a filename. That is the backbone of the timestamped-backup script the exam loves to ask for.

You earned this cheat sheet. Every row is a form you just ran or built:

The one habit to burn in for the exam: $( ) over backticks, always, because it nests and reads clean. And keep $( ) (capture text) separate from $(( )) (do math).

The practice terminal has shown you the shape of command substitution: capture into a variable, splice inline, calculate with $(( )), nest a pipeline inside one $( ), and feed the result into a message, a loop, or a test. Every one of those you typed yourself.

The shell-scripts module mission is where you run these against a real RHEL 10 machine. A full VM boots for you, with its own account list and its own files. The mission hands you the scripting task this lesson was built for: capture a command's output, build a value from it, and use it inside a script that names a file after the date. One difference from this lesson: the mission shows no commands. You read the objective, recall the $( ), and type it. That recall is what makes it stick on exam day.

Finish the other shell-scripts lessons, then go build a real script for yourself.

Practice Capturing Command Output in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.