Courseiva

CCNA Shell Scripts Questions

12 questions · Shell Scripts topic · All types, answers revealed

1
MCQeasy

An administrator writes a script that uses the 'set -e' option at the top. What is the primary effect of this option?

A.It treats unset variables as an error
B.It prints each command before execution
C.It enables debug mode with verbose output
D.It exits the script immediately if a command fails
AnswerD

This is the exact purpose of `set -e`, also known as `errexit`. When enabled, the shell immediately exits if any simple command, pipeline, or compound command (outside of contexts like `if`, `while`, `until`, `!`, or `&&`/`||` left operands) returns a non-zero status. This halts the script at the first error rather than continuing with unchecked failures.

Why this answer

The 'set -e' option instructs the shell to exit immediately if any command or pipeline returns a non-zero exit status (i.e., fails). This is commonly used in scripts to prevent execution from continuing after an error, which could lead to unpredictable behavior or data corruption. It does not affect variable handling, command printing, or debug verbosity.

Exam trap

The trap here is that candidates often confuse 'set -e' with 'set -u' (unset variable errors) or with debugging options like 'set -x' or 'set -v', because all are shell options that begin with 'set -' but have very different effects.

How to eliminate wrong answers

Option A is wrong because treating unset variables as an error is the behavior of 'set -u', not 'set -e'. Option B is wrong because printing each command before execution is the effect of 'set -x' (or 'set -o xtrace'), not 'set -e'. Option C is wrong because enabling debug mode with verbose output is achieved by 'set -v' (or 'set -o verbose'), which prints shell input lines as they are read, not by 'set -e'.

2
MCQmedium

An administrator writes a script to check disk usage and send an alert if usage exceeds 80%. The script uses 'df -h /' and parses the output. To maintain portability and avoid common pitfalls, which approach is recommended?

A.Use 'df -h / | tail -1 | sed 's/.* //' | tr -d '%'
B.Use 'df -h / | tail -1 | cut -d' ' -f5'
C.Use 'df / | awk 'NR==2 {print $5}' | tr -d '%'
D.Use 'df -h / | grep -oP '\d+%'
AnswerC

This option uses `df /` without `-h`, which produces a stable, machine-parseable output. The `awk` command extracts the fifth field (the percentage used) and `tr` removes the percent sign. This is portable across different Unix/Linux systems.

Why this answer

It uses `df /` (without `-h`) to produce a stable, machine-parseable output where the fifth field (`$5`) is always the percentage used, and `awk` reliably extracts it. The `tr -d '%'` removes the percent sign for numeric comparison. This approach avoids the portability issues of parsing human-readable output from `df -h`, which can vary in column spacing and ordering across different Unix/Linux systems.

Exam trap

The trap on the RHCSA exam is that candidates assume `-h` is always better for readability, but the exam tests understanding that human-readable output is unreliable for scripting due to inconsistent column formatting across different Unix/Linux distributions.

How to eliminate wrong answers

Option A is wrong because `sed 's/.* //'` greedily removes everything up to the last space, which fails if the mount point contains spaces or if the output format varies (e.g., long device names). Option B is wrong because `cut -d' ' -f5` splits on single spaces, but `df -h` output often uses multiple spaces or tabs as delimiters, causing `cut` to misinterpret columns. Option D is wrong because `grep -oP` uses Perl-compatible regex, which is not available in all environments (e.g., older systems or minimal installations), and the pattern `\d+%` may match unexpected text like '1%' in a filesystem name.

3
Multi-Selecteasy

Which TWO of the following are valid ways to make a shell script executable?

Select 2 answers
A.bash script.sh
B.chmod 755 script.sh
C.. script.sh
D.chmod u+x script.sh
E.chmod +x script.sh
AnswersB, D

chmod 755 sets permissions to rwxr-xr-x, making the script executable for everyone, which is a valid way to make it executable.

Why this answer

Options B and D are both valid ways to make a shell script executable. Option B uses numeric mode to set execute permission for the owner, while option D uses symbolic mode to add execute for the owner only. Option E is also valid but is not one of the two answers required by the question.

Exam trap

Candidates often think that 'chmod +x' is the only symbolic way, but 'chmod u+x' is also correct. The question requires two answers, and both B and D are valid.

4
MCQmedium

A system administrator needs to create a shell script that checks if the user 'jdoe' exists in the system and, if not, creates the user with a home directory. The script should also verify that the creation was successful. Which of the following script snippets correctly implements this logic?

A.if grep -q '^jdoe:' /etc/passwd; then echo 'Exists'; else useradd 'jdoe' && echo 'Created'; fi
B.if id 'jdoe' &>/dev/null; then echo 'Exists'; else useradd -m 'jdoe' && echo 'Created' || echo 'Failed'; fi
C.if ! id 'jdoe' &>/dev/null; then useradd -m 'jdoe'; else echo 'Exists'; fi
D.[ -z $(id 'jdoe' 2>/dev/null) ] && useradd -m 'jdoe' && echo 'Created'
AnswerB

Correctly checks existence, creates with home dir, and verifies.

Why this answer

It uses `id` to check for the user's existence (redirecting output to /dev/null to suppress messages), then uses `useradd -m` to create the user with a home directory. The `&&` and `||` operators ensure that success or failure of the creation is explicitly reported, fulfilling the requirement to verify successful creation.

Exam trap

Red Hat often tests the misconception that grepping /etc/passwd is sufficient for user existence checks, but the trap here is that modern systems may use remote authentication sources, so `id` is the correct command to query all NSS sources.

How to eliminate wrong answers

Option A is wrong because it greps /etc/passwd for '^jdoe:', which can produce false negatives if the user exists in LDAP or other NSS sources, and it does not create a home directory (missing -m). Option C is wrong because it does not verify that the creation was successful; it only runs useradd without checking its exit status. Option D is wrong because the `[ -z ... ]` test is unreliable (the command substitution may produce unexpected output or errors), and it does not handle the case where the user already exists (it would attempt to create the user again, which would fail).

5
MCQhard

You maintain a script that performs a long-running task and must clean up temporary files if the script is interrupted. The script uses: #!/bin/bash tempfile=$(mktemp) trap "rm -f $tempfile" EXIT # long task sleep 100 You notice that if the script receives SIGINT (Ctrl+C), the temporary file is not removed. Investigation shows that the trap on EXIT is not executed on SIGINT. Which modification should be made?

A.Move the trap inside a subshell: (trap ... EXIT; long task).
B.Change `trap ... EXIT` to `trap ... 0`.
C.Add `set -e` at the beginning of the script.
D.Add a trap for INT: `trap "rm -f $tempfile; exit" INT`.
AnswerD

This is the only option that explicitly handles the signal that is actually interrupting the script. The trap directive installs a handler for SIGINT, so when Ctrl-C is sent, the shell runs "rm -f $tempfile; exit" instead of simply dying. Because the trap is installed at the top level before the long task begins, it remains in effect throughout the task, and the explicit exit prevents the shell from continuing after the cleanup runs. This guarantees the temporary file is removed even on user interruption.

Why this answer

The EXIT trap is only triggered on normal script termination (e.g., reaching the end or an explicit `exit`), not on signals like SIGINT. By adding a separate trap for INT that explicitly removes the temp file and calls `exit`, the cleanup runs even when the user presses Ctrl+C, ensuring the temporary file is deleted.

Exam trap

Red Hat often tests the misconception that the EXIT trap handles all termination scenarios, including signals, when in fact it only runs on normal exit paths, not on unhandled signals like SIGINT.

How to eliminate wrong answers

Option A is wrong because moving the trap inside a subshell would cause the trap to apply only to the subshell, not the main script, and the subshell would exit immediately after the long task, defeating the purpose of the trap. Option B is wrong because `trap ... 0` is exactly equivalent to `trap ... EXIT` in bash; both trigger only on normal exit, not on signals, so this change would not fix the issue.

Option C is wrong because `set -e` causes the script to exit on any command failure, but it does not affect signal handling or trap execution; it would not ensure cleanup on SIGINT.

6
MCQeasy

A developer wants to create a script that accepts a directory path as an argument and creates a timestamped backup of that directory. If no argument is provided, it should back up the current directory. How should the script handle the argument?

A.dir=${1:-.}
B.dir=${@:-.}
C.dir=${0:-.}
D.dir=${?:-.}
AnswerA

The parameter expansion `${1:-.}` explicitly targets the first positional parameter (`$1`) and applies the `:-` operator: if `$1` is unset or null, the expansion substitutes the literal `.` (the current directory). This precisely fulfills the requirement to accept a directory argument with a sensible default when no argument is supplied, because `$1` is the first argument passed to the script, and the default value is only used when that argument is missing or empty.

Why this answer

`${1:-.}` uses the default value substitution syntax in bash: if parameter `$1` (the first positional argument) is unset or null, it expands to `.` (the current directory). This ensures the script backs up the supplied directory path or defaults to the current directory when no argument is provided, exactly matching the requirement.

Exam trap

Red Hat often tests the distinction between positional parameters (`$1`, `$2`, etc.) and special variables (`$@`, `$0`, `$?`), and the trap here is that candidates confuse `$1` with `$0` (the script name) or incorrectly assume `$@` works as a single default value, leading to option B or C.

How to eliminate wrong answers

Option B is wrong because `${@:-.}` expands to all positional arguments (`$@`) or `.` if none are provided, but `$@` is a list, not a single directory path, and using it in a backup command would break the script. Option C is wrong because `${0:-.}` refers to the script's own name (the zeroth argument), not the first argument passed by the user, so it would always expand to the script name instead of the intended directory. Option D is wrong because `${?:-.}` is not valid bash syntax; `$?` holds the exit status of the last command, and the `:-` substitution does not apply meaningfully here, causing a syntax error or unintended behavior.

7
MCQmedium

A script needs to execute a command that might fail, but the script should continue. The administrator wants to capture the exit status for logging. Which code snippet correctly implements this?

A.set -e; ./risky_command; rc=$?; echo $rc
B.rc=$? ./risky_command; echo $rc
C../risky_command; rc=$?; echo $rc
D../risky_command && rc=$?; echo $rc
AnswerC

The semicolon is a command terminator that allows rc=$? to execute regardless of how risky_command exited. After risky_command finishes, $? holds its exact exit status, and the assignment immediately stores it before any other command or expansion can alter it. The subsequent echo $rc then prints the saved value, making this the correct pattern for capturing a failure status without terminating the script.

Why this answer

It runs the risky command, captures its exit status immediately after in the `$?` variable, and then echoes it for logging. The script continues regardless of the command's success or failure, which meets the requirement. The `$?` variable holds the exit status of the last executed foreground command, so assigning it to `rc` right after `./risky_command` ensures the correct value is stored.

Exam trap

In Red Hat Enterprise Linux shell scripting, a common mistake is to use `set -e` or conditional operators like `&&` when the goal is to capture the exit status regardless of success or failure. The correct approach is to assign `$?` unconditionally immediately after the command.

How to eliminate wrong answers

Option A is wrong because `set -e` causes the shell to exit immediately if any command fails, which contradicts the requirement that the script should continue after a failure. Option B is wrong because `rc=$? ./risky_command` sets `rc` in the environment of `./risky_command` (not the current shell) and `$?` is evaluated before the command runs, so `rc` gets the exit status of the previous command, not `./risky_command`. Option D is wrong because `&&` makes the assignment `rc=$?` conditional on `./risky_command` succeeding; if the command fails, `rc` is never assigned, and the exit status is lost.

8
MCQeasy

A system administrator needs to create a shell script that processes a list of hostnames stored in a file, one per line, and runs a command on each host. Which loop construct is most appropriate?

A.while read host; do ... done < hosts
B.for i in $(seq 1 $(wc -l < hosts)); do ... done
C.for host in $(cat hosts); do ... done
D.until read host; do ... done < hosts
AnswerA

The `while read host; do ... done < hosts` construct is the correct approach because it reads the file line by line. On each iteration, `read` assigns the entire line (minus trailing newline) to the variable `host`, so the content is not subjected to word splitting or pathname expansion. This makes it robust for hostnames that might contain unusual characters, though for exact preservation one should add `IFS=` and `-r` to `read`. Additionally, the redirection `< hosts` attaches the file to the loop's standard input, and the loop runs in the current shell, so any variable updates inside the loop remain available afterward.

Why this answer

The `while read host; do ... done < hosts` construct reads the file line by line, preserving each hostname exactly as it appears, including spaces or special characters. This is the safest and most idiomatic way to process a list of hostnames in a shell script, as it avoids word splitting and glob expansion issues that can occur with other methods.

Exam trap

The trap here is that candidates often choose `for host in $(cat hosts)` because it looks simpler, but they overlook how word splitting and glob expansion can break the script when hostnames contain spaces, tabs, or wildcard characters.

How to eliminate wrong answers

Option B is wrong because it uses a for loop with `seq` and `wc -l`, which is unnecessarily complex and fragile; it requires counting lines first and then indexing into the file, which is error-prone and not a standard pattern for reading lines. Option C is wrong because `for host in $(cat hosts)` subjects the file content to word splitting and glob expansion, so hostnames with spaces or wildcard characters would be incorrectly split or expanded. Option D is wrong because `until read host` is syntactically invalid; `until` tests a condition at the top of the loop, but `read` returns a non-zero exit status at end-of-file, making the loop never execute its body (or execute it incorrectly), and the redirection `< hosts` is misplaced.

9
MCQhard

A complex script uses 'trap' to handle signals. The admin writes 'trap '' SIGINT' to ignore Ctrl+C, but later in the script they want to re-enable the default behavior. Which command restores the default behavior for SIGINT?

A.trap - SIGINT
B.trap : SIGINT
C.trap 2
D.trap SIGINT
AnswerA

Correct. `trap - SIGINT` removes the custom handler and restores the default behavior (terminating the process).

Why this answer

`trap - SIGINT` resets the signal handler for SIGINT to its default behavior. The `trap '' SIGINT` command sets an empty action, which ignores the signal; using `trap - signal` removes that custom handler and restores the default action (typically terminating the process).

Exam trap

The RHCSA exam often tests the subtle distinction between `trap '' signal` (ignore) and `trap - signal` (restore default), where candidates mistakenly think `trap signal` or `trap : signal` resets the handler.

How to eliminate wrong answers

Option B is wrong because `trap : SIGINT` sets the action to the null command `:`, which is a no-op that still ignores the signal (similar to `trap '' SIGINT`), not restoring the default. Option C is wrong because `trap 2` is invalid syntax; signal numbers must be preceded by a dash or used with a signal name, and this would attempt to set a command '2' for the signal, causing an error or unintended behavior. Option D is wrong because `trap SIGINT` without a command or dash is ambiguous and typically results in an error or sets the trap to an empty string, depending on the shell, but does not restore the default handler.

10
MCQmedium

A developer runs the script shown in the exhibit and always sees 'Success' printed, even when the previous command fails. What is the most likely cause?

A.The [[ ]] syntax always evaluates to true
B.The $? variable is only set after external commands, not builtins
C.The $? variable captures the exit status of the [[ command, not the intended command
D.The $? variable always returns 0 in a conditional
AnswerC

In the given script, if an intended command is followed by a [[ ... ]] test, $? will reflect the exit status of the [[ ]] evaluation, not the earlier command. For example, if the script does [[ -f file ]] after running an application, $? becomes 1 when file does not exist, even if the application succeeded. Because $? is overwritten by each subsequent command, any intervening conditional destroys the original exit value.

Why this answer

The `[[ ]]` conditional construct is a shell keyword that itself produces an exit status. When `$?` is checked immediately after `[[ ]]`, it captures the exit status of the `[[ ]]` evaluation (which is 0 if the condition is true, 1 if false), not the exit status of the command that was run before the `[[ ]]`. Since the developer always sees 'Success' printed, the `[[ ]]` condition must be evaluating to true (exit status 0), causing `$?` to be 0 and the script to always take the success path, regardless of the actual previous command's result.

Exam trap

The trap here is that candidates mistakenly think `$?` always reflects the original command's exit status, not realizing that `[[ ]]` is itself a command that resets `$?` to its own exit status, causing the check to always succeed if the `[[ ]]` expression is syntactically valid.

How to eliminate wrong answers

Option A is wrong because `[[ ]]` does not always evaluate to true; it evaluates to true (exit status 0) or false (exit status 1) based on the expression inside it. Option B is wrong because `$?` is set after every command, including shell builtins like `[[ ]]`, `[ ]`, and `echo`; it is not limited to external commands. Option D is wrong because `$?` does not always return 0 in a conditional; it returns the exit status of the most recently executed command, which can be non-zero if that command failed.

11
MCQeasy

A developer wrote a shell script that is intended to back up log files by copying all .log files from /var/log/myapp to /backup/logs. The script runs daily via cron but the backup folder is empty. The script contains the following line: `cp /var/log/myapp/*.log /backup/logs/`. What is the most likely reason the backup fails?

A.The PATH variable in cron is not set, so cp cannot be found.
B.The script does not have execute permission for the user running cron.
C.No .log files exist in /var/log/myapp at the time of script execution, causing the glob to match nothing.
D.The cron job is not enabled because the crontab syntax is incorrect.
AnswerC

In a non-interactive shell, an unmatched glob like /var/log/myapp/*.log is not expanded and is passed literally to cp. cp then attempts to copy a file named `*.log`, which does not exist, producing a 'No such file or directory' error and creating no backup. Unless the script checks the glob result or has error handling, this failure can be silent, especially if cron's stderr output is not inspected.

Why this answer

The glob pattern `*.log` in the `cp` command is expanded by the shell at the time the script runs. If no `.log` files exist in `/var/log/myapp` when the cron job executes, the shell passes the literal string `*.log` to `cp`, which then fails with a 'No such file or directory' error (or, depending on shell settings, may silently do nothing). This is a common issue when log rotation or cleanup removes files before the backup runs.

Exam trap

Red Hat often tests the misconception that cron PATH or permissions are the root cause, but the real trap is that glob expansion happens at script execution time and an empty glob silently fails, leading to an empty backup destination.

How to eliminate wrong answers

Option A is wrong because `cp` is a built-in shell command or located in standard paths like `/bin/cp` or `/usr/bin/cp`, and cron typically sets a minimal PATH that includes `/usr/bin` and `/bin`, so `cp` is almost always found. Option B is wrong because the script itself does not need execute permission if it is invoked via `sh script.sh` or if the cron job line directly calls `sh`; the issue is about file existence, not permissions. Option D is wrong because the question states the script runs daily via cron, implying the crontab syntax is correct and the job is enabled; the backup folder is empty, not that the job fails to run.

12
MCQeasy

Consider the script in the exhibit. The script is run in a directory containing 'a.txt' and 'b.txt' but also has a subdirectory 'backup' with .txt files. What will be the output?

A.An error because the for loop cannot iterate over files with spaces
B.Line counts for .txt files in 'backup' only
C.Line counts for 'a.txt' and 'b.txt' only
D.Line counts for all .txt files including those in 'backup'
AnswerC

The shell expands `*.txt` to the names of matching files in the current directory, which in the given directory listing are `a.txt` and `b.txt`. The `for i in` loop assigns each name in turn to `i`, and `wc -l "$i"` prints the line count for each file. Files in subdirectories such as `backup` are not matched because globbing is not recursive unless `**` is used.

Why this answer

The script uses `for i in *.txt`, which by default only matches .txt files in the current directory, not in subdirectories. Since 'a.txt' and 'b.txt' are in the current directory, the loop iterates over them and runs `wc -l` on each, outputting their line counts. The 'backup' subdirectory is not traversed because the glob pattern does not include paths with directories.

Exam trap

Red Hat often tests the candidate's understanding that shell glob patterns like `*.txt` do not recurse into subdirectories, leading many to incorrectly assume that all .txt files in the entire directory tree are processed.

How to eliminate wrong answers

Option A is wrong because the for loop can iterate over files with spaces if the glob pattern is unquoted and the files are properly handled (though the script does not quote $i, which could cause issues with spaces, but the question states no files have spaces, so no error occurs). Option B is wrong because the glob `*.txt` does not match files in the 'backup' subdirectory; it only matches .txt files in the current directory. Option D is wrong because the glob pattern does not recursively include files in subdirectories; only files directly in the current directory are matched.

Ready to test yourself?

Try a timed practice session using only Shell Scripts questions.