Learn › Linux Foundations › Chaining
{ } - a hands-on Linux lab on a real virtual machine.
Bundle commands into one unit with a brace group { }. See why braces run in your current shell so a variable or a cd sticks, contrast it with the subshell ( ) that throws its changes away, read a group's exit code, and guard a whole group with one operator, all in the practice terminal.
You have been chaining commands with ;, &&, and ||. Each of those joins commands one after another. But sometimes you want to treat several commands as a single unit. You run them as a group, then send that whole group's result into one && or ||, or capture the group's success or failure all at once.
Linux gives you two ways to bundle commands into a group. This lesson is about one of them, the brace group, written with curly braces { ... }. Along the way you will meet its close cousin, the subshell ( ... ), because the single most important thing about braces only makes sense next to it.
The black boxes below are a practice terminal: a safe sandbox that only checks the one command each step teaches, so you cannot break anything. On the real machine at the end of this module, the mission drills these same groups for real.
A brace group runs a list of commands as one unit. The syntax has two rules that trip up almost everyone the first time, so learn them now:
{.;, including the last one before the closing }.So the shape is always { command; command; }. Run a simple one that prints two lines:
{ echo a; echo b; }
prompt: student@linuxcamp:~$ answer: { echo a; echo b; } output: a b hint: Mind the two rules: a space after the opening brace, and a semicolon after every command including the last. Type it exactly: { echo a; echo b; }
Both echo commands ran, and their output came out in order: a, then b. Nothing surprising yet, the group just ran the commands inside it. The interesting part is not WHAT it runs, it is WHERE it runs them. That is the next step, and it is the whole point of this lesson.
Here is the defining fact about a brace group: it runs the commands in your current shell. It does not start a new one. Anything the group changes, a variable, your current folder, sticks around after the group finishes, because there was only ever one shell the whole time.
Watch a variable survive the group. A variable is just a named value; Y=kept creates one called Y holding the text kept. Set it inside a brace group, then read it back outside with echo:
{ Y=kept; }; echo "after brace: Y=$Y"
prompt: student@linuxcamp:~$ answer: { Y=kept; }; echo "after brace: Y=$Y" output: after brace: Y=kept hint: A brace group that sets Y, then a semicolon, then an echo that reads it back: { Y=kept; }; echo "after brace: Y=$Y"
The value stuck. You set Y=kept inside the braces, and outside the braces $Y still reads kept. That is because the brace group never left your shell. Hold onto that result, because in the very next step you will run the near-identical command with the OTHER kind of group and get a completely different answer.
The subshell is the other way to group commands, written with parentheses ( ... ). It looks almost the same, but it does the opposite of a brace group in the one way that matters: a subshell runs the commands in a separate shell of its own. When that shell finishes, everything it changed is thrown away.
Set a variable X inside a subshell, print it from inside, then try to read it from outside:
( X=inside; echo "inside subshell: X=$X" ); echo "outside subshell: X=$X"
prompt: student@linuxcamp:~$ answer: ( X=inside; echo "inside subshell: X=$X" ); echo "outside subshell: X=$X" output: inside subshell: X=inside outside subshell: X= hint: Parentheses this time. Set X and echo it inside, then echo it again outside: ( X=inside; echo "inside subshell: X=$X" ); echo "outside subshell: X=$X"
Look at the two lines. Inside the parentheses, X was inside, exactly as set. But outside, X came back empty: the X= at the end of the second line has nothing after it. The subshell set X in its own separate shell, and that shell vanished the instant the ) closed, taking X with it. Put the two steps side by side: braces { } kept Y, parentheses ( ) lost X. Same-shell versus separate-shell, that is the entire difference.
The same rule governs your current folder, the directory your shell is sitting in. Because a brace group runs in your current shell, a cd inside it moves you for real and you stay moved. Prove it in three plain steps: check where you are, cd inside a brace group, then check again.
For this drill you are standing in a practice folder called chain-lab, the same folder the mission at the end of this module builds for you. It contains a logs folder to move into. Start by asking where you are standing. The pwd command prints your current folder:
pwd
prompt: student@linuxcamp:~/chain-lab$ answer: pwd output: /home/student/chain-lab hint: Just three letters, then press Enter: pwd
Now cd into the logs folder from inside a brace group. Remember, cd prints nothing when it works, so expect a silent, blank result:
{ cd logs; }
prompt: student@linuxcamp:~/chain-lab$ answer: { cd logs; } output: hint: A brace group with a single cd inside, space after the brace and semicolon before the close: { cd logs; }
Blank output is success here. cd never prints anything when it works, so you confirm the move with a separate pwd, which is exactly the next step. The absolute path pwd reports will start with your own home folder, so your exact path may read differently from the example; what matters is the SHAPE of the change.
Ask pwd again to see whether the brace group's cd actually moved you:
pwd
prompt: student@linuxcamp:~/chain-lab/logs$ answer: pwd output: /home/student/chain-lab/logs hint: The same three letters as before: pwd
The second pwd is one level deeper: it now ends in /logs. The cd inside the braces stuck, because braces run in your current shell. If you had used a subshell instead, ( cd logs ), the cd would have moved only the subshell, and this second pwd would read exactly the same as the first, unchanged. Braces move you and keep you moved; a subshell wanders off on its own and leaves you right where you started. Read the difference by the SHAPE of the path, deeper versus unchanged, not by the exact folder name.
Every command in Linux reports an exit code when it finishes: 0 means success, any other number means failure. A whole group reports one too, and it is simply the exit code of the last command inside it.
The $? variable holds the exit code of the command that just ran. The false command does nothing except fail on purpose, so it always exits 1. Put false in a brace group and read the group's exit code back with echo $?:
{ false; }; echo $?
prompt: student@linuxcamp:~$ answer: { false; }; echo $? output: 1 hint: A brace group holding only false, then read the exit code: { false; }; echo $?
The group reported 1, the exit code of false, the last (and only) command inside it. That single number is the group's verdict, and it is what the next && or || in a chain will look at. A subshell works the same way for exit codes: ( false ); echo $? also prints 1. Exit codes are the one thing braces and parentheses agree on completely.
Now the payoff for bundling. Because a group reports a single exit code, you can hang an && or || off the whole group instead of one command. The || operator runs its right side only if the left side failed. Attach a fallback to a brace group that succeeds, and watch the fallback stay quiet:
{ echo step1; echo step2; } || echo failed
prompt: student@linuxcamp:~$ answer: { echo step1; echo step2; } || echo failed output: step1 step2 hint: A brace group of two echoes, then || and a fallback echo: { echo step1; echo step2; } || echo failed
Both echoes ran, and the word failed never appeared. The group's last command, echo step2, succeeded, so the whole group's exit code was 0, so || skipped its fallback. You just treated two commands as one unit and guarded them with a single operator. That is the reason grouping exists: it lets one && or || decide the fate of a whole block at once. Now prove the whole kit is yours: three challenges, no commands shown.
Scaffolding off. No command is shown from here on.
Prove that a brace group runs in your current shell. Set a variable named Y to the word kept inside a brace group, then, outside the group on the same line, print the label after brace: Y= followed by the variable's value. If the value survived, the line ends in kept.
prompt: student@linuxcamp:~$ answer: { Y=kept; }; echo "after brace: Y=$Y" output: after brace: Y=kept hint: A brace group that assigns Y, then a semicolon, then an echo in double quotes that reads $Y back. Remember the space after { and the ; before }.
The line ends in kept, so the assignment survived the braces. From memory this time: only one shell existed the whole way through, so nothing was lost. Had you wrapped the assignment in parentheses instead, that final Y= would have come back empty.
Still no command shown. Build a brace group holding only the command that does nothing except fail on purpose, then read the group's exit code back with the variable that holds the last exit code.
prompt: student@linuxcamp:~$ answer: { false; }; echo $? output: 1 hint: The always-fail command lives between the braces; after the group, echo the $? variable.
1, the exit code of false, reported by the whole group. A group's verdict is always its last command's code, and that single number is what any && or || after the group will judge.
One more, from memory. Group two commands that print step1 and step2, then attach a fallback that prints failed, but only if the group fails. If you build it right, both step lines appear and the word failed never does.
prompt: student@linuxcamp:~$ answer: { echo step1; echo step2; } || echo failed output: step1 step2 hint: A brace group of two echoes, then the run-on-failure operator, then the fallback echo.
Both steps printed and failed stayed silent, exactly the sign of a healthy guarded block. You recalled all three moves without a single command shown: same-shell persistence, the group's exit code, and one operator guarding a whole block. The kit is yours.
You earned this cheat sheet. Every row is something you just ran in the practice terminal:
The one idea that separates the two kinds of group: braces { } run in your current shell, so their changes stick. A subshell ( ) runs in a separate shell, so its changes disappear. Two syntax rules keep braces happy: a space after {, and a ; before }. Parentheses need neither.
Reach for a subshell ( ) when you want to do something messy, cd around, set throwaway variables, and come back with your shell untouched. Reach for a brace group { } when you want the changes to stay. It also fits when you just need to gang a few commands under a single && or || without spawning a whole new shell.
The practice terminal has walked you through the whole story of grouping. You learned the two syntax rules for braces, how a brace group keeps a variable and a cd while a subshell throws both away, how a group carries the exit code of its last command, and how to guard an entire group with one ||. Every one of those you typed yourself and saw the real result.
This module ends with one real Linux machine: the chaining mission lab. There a VM boots just for you, and the mission hands you objectives built from exactly what you practiced here: sequencing, && and ||, subshells, brace groups, and exit codes. It shows no commands. You read the objective, recall the move, and type it. That recall is what turns this reading into a skill.
Finish the other Chaining lessons, then go run it for real.
Practice {} Brace Grouping in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.