Run grep -q '^enabled$' state.txt.
Run echo DISABLED only when there is no exact match.
Assume state.txt is a readable regular file and grep encounters no execution or input errors.
Which command list is correct?
An OR list runs its right-hand command after any nonzero exit status. Under the stated readable-file assumption, grep status 1 means no match; an input error can also trigger ||.
Detailed explanation
grep -q '^enabled$' state.txt && echo DISABLEDIncorrect. && runs the right side after a successful match.
The AND list prints DISABLED after a successful match and skips it after no match. Under the stated premise, this reverses the required condition.
grep -q '^enabled$' state.txt || echo DISABLEDCorrect. echo runs when grep finds no matching line.
With the stated readable file, grep returns one for no match and the fallback runs. The operator is not specific to one: a missing-file error returning two also triggers it outside the question’s premise.
grep -q '^enabled$' state.txt; echo DISABLEDIncorrect. ; prints DISABLED for both a match and a non-match.
The semicolon runs echo regardless of grep’s result. A matching file still produces DISABLED, so testing only a no-match case would miss the defect.
grep -q '^enabled$' state.txt | echo DISABLEDIncorrect. A pipe connects output streams; it does not test grep's status this way.
The echo command on the right of the pipe is executed as a pipeline command. grep -q suppresses normal matching-line output, and echo does not use input data as a condition. Enabling pipefail does not make execution of echo conditional.
Try it yourself
An example you can run in a temporary verification environment.
LAB_DIR=$(mktemp -d) || exit 1
(
cd "$LAB_DIR" || exit 1
printf 'enabled\n' > state.txt
grep -q '^enabled$' state.txt
printf 'match status=%s\n' "$?"
grep -q '^enabled$' state.txt || printf 'match: DISABLED\n'
printf 'disabled\n' > state.txt
grep -q '^enabled$' state.txt
printf 'no-match status=%s\n' "$?"
grep -q '^enabled$' state.txt || printf 'no-match: DISABLED\n'
grep -q '^enabled$' missing.txt 2>/dev/null
printf 'error status=%s\n' "$?"
grep -q '^enabled$' missing.txt 2>/dev/null || printf 'error: DISABLED\n'
rm -- state.txt
)
rmdir -- "$LAB_DIR"Official references
- Official documentation 1
- Official documentation 2
- Official documentation 3
- Official documentation 4
- Official documentation 5
- Official documentation 6
Expected result
match status=0
no-match status=1
no-match: DISABLED
error status=2
error: DISABLEDKey points
- Short-circuit evaluation with ||
- grep -q exit status
- Fallback handling
Notes
- Environment: Bash 5.2 / GNU grep 3.x / GNU coreutils in a fresh process with errexit and pipefail off. Separate the readable single-file question from the intentional missing-file error example.
- The match and no-match checks satisfy the question’s premise. The missing.txt checks are synthetic error examples outside that premise; only their diagnostic text is suppressed so the stable status two can be compared.
- GNU grep -q can return zero after finding a match even if an error occurred with another input. Do not treat the normal 0/1/2 distinction as an exception-free claim about all inputs.
Foundation review
No match and an input error are different
For a readable single file, compare match status zero with no-match status one. A missing-file error returning two also triggers ||; production logic requiring no match alone must explicitly distinguish status one.
Observe the original status immediately
The status of grep || echo is the status of the last command actually executed. A successful fallback can make the list return zero, so its final $? is not the original grep status.
Pipes and conditional lists have different roles
A pipe connects data streams, while || controls execution using a nonzero status. Changing a pipeline’s resulting status with pipefail does not make the two operators equivalent.