Bash & the POSIX Shell Module 14 – Portability: POSIX sh, versions and platforms
What POSIX.1-2024 changed for shell scripts
Read what POSIX.1-2024 added to the shell language and utilities, see what bash 5.3.20 and dash 0.5.13.5 run, and probe for Issue 8 features safely.
What you will learn
- List the POSIX.1-2024 (Issue 8) changes that affect everyday scripts
- Test which ones bash 5.3.20 and dash 0.5.13.5 support today
- Use the new features without breaking older systems
Before you start
What you’ll learn · Before you start
What you will learn
- List the POSIX.1-2024 (Issue 8) changes that affect everyday scripts
- Test which ones bash 5.3.20 and dash 0.5.13.5 support today
- Use the new features without breaking older systems
Before you start
On this page
A script written for POSIX.1-2024 may lean on features that a POSIX.1-2017 shell never had to provide: of the seven
probed below, dash 0.5.13.5 ran four and the bash 5.3.20 printf builtin refused one. POSIX.1-2024, also called
Issue 8: The eighth edition of The Open Group Base Specifications, published jointly as IEEE Std 1003.1-2024 (POSIX.1-2024). It replaces Issue 7 and adds dollar-single-quotes, pipefail, trap -p, a Job Control section (2.11) and new behaviour to read, test and printf., changed the shell language in four ways that scripts meet first: $'...' quoting,
set -o pipefail, a Job Control section and trap -p. It also gave the utilities read -d, the test operators
-nt, -ot and -ef and numbered printf conversions such as %2$s, and it added new programs, among them
realpath, readlink and timeout.
The sections below place each change in its edition, read the recorded runs of bash 5.3.20 and dash 0.5.13.5 against the standard, and end with a guard that switches an Issue 8 option on only where the running shell has it.
Issue 8 mostly adds to the Issue 7 shell standard
The shell every POSIX system runs is specified in one volume of The Open Group Base Specifications, which The Open Group publishes together with the IEEE. The whole set is revised in editions called issues, and each issue is also an IEEE Std 1003.1 edition. Issue 7: The seventh edition of The Open Group Base Specifications, aligned with IEEE Std 1003.1-2008 and later corrigenda (POSIX.1-2017). Debian policy still names this edition as the baseline for /bin/sh scripts. began as IEEE Std 1003.1-2008, and its 2018 edition, IEEE Std 1003.1-2017, a revision of that text, is POSIX.1-2017; Issue 8 is IEEE Std 1003.1-2024. Each Issue 8 page ends with a change history that names the Austin Group defects behind its changes, which is where the defect numbers in this lesson come from. Walk through the three editions a script is most likely to meet, oldest first:
- Issue 6
An earlier edition of the base specifications base
The sixth edition of The Open Group Base Specifications, matching IEEE Std 1003.1-2001. The shell features most scripts lean on were already there: parameter expansion, the test utility, pipelines, and the set options -e and -u.
- Issue 7
POSIX.1-2017, the edition Debian policy names baseline
The seventh edition began as IEEE Std 1003.1-2008, and its 2018 edition is IEEE Std 1003.1-2017, a revision of that text with two technical corrigenda applied. The Debian Policy Manual still lets a /bin/sh script assume this edition plus a few extras it names, so a Debian package may not rely on what Issue 8 added.
- Issue 8
POSIX.1-2024, the newest edition newest
The eighth edition, published as IEEE Std 1003.1-2024. It adds dollar-single-quotes, set -o pipefail, a Job Control section (2.11) and trap -p to the shell language, new options to read, test and printf, and the new utilities realpath, readlink and timeout. Shells adopt its changes at their own pace.
The timeline has 3 events, from Issue 6 to Issue 8.
Each edition mostly adds to the one before it, so a script written for Issue 7 keeps working in an Issue 8 shell. The
few removals had been marked obsolescent first: Issue 8 dropped the -a and -o operators and the parentheses of
test (Austin Group Defect 1330), so a script that still uses them relies on its shell rather than on the standard.
The risk runs the other way: a script written against Issue 8 may use a feature that a shell built for Issue 7 never
had.
Policies lag the standard too. The Debian Policy Manual still lets a /bin/sh script assume POSIX.1-2017 plus a few
extras it names, such as echo -n, local and the very -a and -o of test that Issue 8 removed. The
POSIX sh versus Bash lesson shows how to choose the first line of a
POSIX shell: A shell that implements the shell command language the POSIX standard defines for the sh utility. Bash and dash both do; Bash adds many extras the standard does not require. More in “POSIX sh vs Bash: spotting and fixing bashisms” script; this one adds the question of which edition that script may rely on.
Why Issue 8 does not mean portable
A feature in the standard is a promise about shells that conform to Issue 8, not a report on the shells already installed. The manual of the dash 0.5.12 that Ubuntu 24.04 ships does not list a pipefail option at all.
The probes in the next sections measure that gap one feature at a time, so the matrix later on shows both what Issue 8 asks for and what bash 5.3.20 and dash 0.5.13.5 already run.
Key idea
Issue 8 mostly adds to Issue 7, so a script that must also run on older shells can use each new feature only after checking that the running shell has it.
Checkpoint
Check: Issue 7 and Issue 8 editions
Four shell-language changes reach scripts first
Four changes to the shell language matter most to everyday scripts, because the shell reads them itself before any utility runs:
- ANSI-C quoting: The $'…' form of quoting, in which backslash escapes such as \t, \n, \x41 and \u00e9 become the characters they name; part of POSIX.1-2024 and supported by Bash and dash. More in “Quoting: single, double, backslash and $'...'”, section 2.2.4 Dollar-Single-Quotes: inside
$'...', escapes such as\t,\n,\xHHand\dddturn into the bytes they name just before the word is expanded. - pipefail: Bash's set -o pipefail option: the exit status of a pipeline is the rightmost non-zero stage's status, or 0 when every stage succeeded. A stage killed by SIGPIPE (because its reader closed early) is reported as 128 + 13 = 141. More in “set -e, set -u and pipefail: what they really do”, an option of
setadded by Austin Group Defect 789: with it on, a pipeline takes the status of its rightmost stage that failed, so one failing stage is enough to fail the whole pipeline. - Section 2.11 Job Control, new in Issue 8: it gathers the shell’s job-control rules, from
set -mto thefgandbgutilities and the signals that stop and continue a job, in one place. trap -p, added by Austin Group Defects 1211 and 1212. Plaintrapalready listed the traps a script had set;-pprints the action of each condition you name, or of every condition, as commands the shell can read back in.
The next session runs seven probes under bash 5.3.20, the same bash in POSIX mode, and dash 0.5.13.5. Each prints
supported when the shell does what Issue 8 asks; the first three lines of every block are the shell-language probes:
from session import run, write
# Seven probes, one per POSIX.1-2024 addition the lesson names. The runner starts each probe under three shells:
# GNU Bash 5.3.20 (built from source), the same Bash with --posix, and dash 0.5.13.5. It prints "supported" when the
# probe does what POSIX.1-2024 says and "unsupported" when it does not, so the table stays stable between runs.
# Every probe sends its error messages to /dev/null: the table records what each shell did, not how it complained.
write("probe.sh", r"""set +e
label=$1
case $label in
dollar_single)
out=$(printf %s $'a\tb') ; expected=$(printf 'a\tb')
[ "$out" = "$expected" ] && echo supported || echo unsupported
;;
pipefail)
(set -o pipefail) 2>/dev/null || { echo unsupported; exit 0; }
(set -o pipefail; false | true) >/dev/null 2>&1
[ $? -ne 0 ] && echo supported || echo unsupported
;;
trap_p_exit)
trap 'echo bye' EXIT 2>/dev/null
out=$(trap -p EXIT 2>/dev/null)
case $out in *bye*) echo supported;; *) echo unsupported;; esac
trap - EXIT 2>/dev/null
;;
read_d_delim)
out=$(printf 'a:b:c' | { IFS= read -r -d : first 2>/dev/null && echo "$first"; }) 2>/dev/null
[ "$out" = a ] && echo supported || echo unsupported
;;
test_nt_ot)
: > f2 ; sleep 1 ; : > f1
if test f1 -nt f2 2>/dev/null && test f2 -ot f1 2>/dev/null; then echo supported; else echo unsupported; fi
rm -f f1 f2
;;
test_ef)
: > f1
if test f1 -ef f1 2>/dev/null; then echo supported; else echo unsupported; fi
rm -f f1
;;
printf_numbered)
out=$(printf '%1$s\n' x 2>/dev/null)
[ "$out" = x ] && echo supported || echo unsupported
;;
esac
""")
write("sweep.sh", r"""shell=$1
for p in dollar_single pipefail trap_p_exit read_d_delim test_nt_ot test_ef printf_numbered; do
printf ' %-16s %s\n' "$p" "$($shell probe.sh $p)"
done
""")
run("""
# bash 5.3.20: which of the seven POSIX.1-2024 probes pass
echo "== bash 5.3.20 =="
bash sweep.sh bash
# the same bash in POSIX mode: it changes what differs from the standard and keeps its own features
echo "== bash --posix =="
bash sweep.sh "bash --posix"
# dash 0.5.13.5, a recent release of the shell that Ubuntu installs as sh
echo "== dash 0.5.13.5 =="
bash sweep.sh dash
""") Output
# bash 5.3.20: which of the seven POSIX.1-2024 probes pass $ echo "== bash 5.3.20 ==" == bash 5.3.20 == $ bash sweep.sh bash dollar_single supported pipefail supported trap_p_exit supported read_d_delim supported test_nt_ot supported test_ef supported printf_numbered unsupported # the same bash in POSIX mode: it changes what differs from the standard and keeps its own features $ echo "== bash --posix ==" == bash --posix == $ bash sweep.sh "bash --posix" dollar_single supported pipefail supported trap_p_exit supported read_d_delim supported test_nt_ot supported test_ef supported printf_numbered unsupported # dash 0.5.13.5, a recent release of the shell that Ubuntu installs as sh $ echo "== dash 0.5.13.5 ==" == dash 0.5.13.5 == $ bash sweep.sh dash dollar_single supported pipefail supported trap_p_exit unsupported read_d_delim unsupported test_nt_ot supported test_ef supported printf_numbered unsupported
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 issue8_matrix_session.py
bash 5.3.20 passed all three shell-language probes in both modes. dash 0.5.13.5 passed $'...' and pipefail and
failed trap -p EXIT: the probe set an EXIT trap, asked trap -p EXIT for it and got nothing back. Its manual gives
trap an action and signals only, with no -p option. Job Control describes behaviour rather than adding a
command, so no probe covers it.
bash --posix printed the same column as plain bash. The Bash manual says POSIX mode changes Bash only where its
default differs from the standard, to make Bash a strict superset of it, so Bash keeps its own features in that mode.
That is why a run under bash --posix cannot stand in for dash: trap -p and read -d pass there and fail in dash.
Guessing which column dash fills in
Wrong: reading Issue 8 as a list of what dash runs. Right: run the probe on the shell itself. The pipefail row
printed supported on dash and the trap -p row did not, although both features arrived in the same edition.
Checkpoint
Check: Shell-language additions in Issue 8
The utilities gained options, and new programs arrived
Issue 8 changed the utility pages as well. read gains -d delim, which ends the record at a single byte of your
choice instead of the newline (Austin Group Defect 243). test gains -nt and -ot, which compare the modification
times of two files, and -ef, which is true when two paths lead to the same file (Defect 375). printf gains numbered
conversions such as %1$s and %2$s, which pick an argument by its position (Defect 1592). Among the programs new in
Issue 8, three serve scripts directly: realpath turns a path into its canonical form, readlink prints the target
of a symbolic link and timeout runs a command under a time limit. Issue 8 also made options standard that scripts
had long used, among them find -print0, xargs -0 and sed -E.
Step through what read -d : changes for one short input:
The drawing shows the whole scene; its 4 steps are listed below.
Every step as text
- The whole string reaches read. A pipeline sends the bytes a, colon, b, colon, c to read. There is no newline in the data, so where read stops depends on what counts as the end of a record.
- Issue 7 read stops at a newline. In Issue 7 the only record delimiter read knows is the newline. The input has none, so the first variable takes every byte, and the colons stay part of the value.
- Issue 8 adds -d delim. The Issue 8 read page adds -d delim: a delim of one single-byte character takes the place of the newline, and an empty delim means the null byte. With -d : the record ends at the first colon.
- first now holds one field. In the probe, printf a:b:c | { IFS= read -r -d : first; echo "$first"; } printed a under bash 5.3.20. dash 0.5.13.5 failed the same probe, and its manual gives read only -p and -r, so a script for that dash keeps an IFS=: read -r a b c line instead.
The probes split along the same lines. dash printed supported for both test rows, whose operators its manual
already lists, and unsupported for read -d: its manual gives read only the -p and -r options. Both shells
printed unsupported for numbered printf, because the printf builtins of bash and dash both refused the directive.
The next session hands one numbered format to both builtins and to the external printf of macOS:
from session import run, write
# POSIX.1-2024 added numbered printf conversions (such as %2$s and %1$s) to the printf utility. On this Mac three
# printfs are compared: the Bash 5.3.20 builtin and the dash 0.5.13.5 builtin, which both reject the directive, and
# /usr/bin/printf, the BSD printf of macOS 26, which prints the arguments by position. The exit status is kept with the recorded output so the session
# shows where each printf stopped.
write("show.sh", r"""set +e
echo "== bash 5.3.20 builtin printf =="
printf '%2$s %1$s\n' A B
echo "[exit status $?]"
echo "== /usr/bin/printf on macOS =="
/usr/bin/printf '%2$s %1$s\n' A B
echo "[exit status $?]"
""")
# The same three lines for the dash builtin: dash names the script and the line in its message.
write("show-dash.sh", r"""set +e
echo "== dash 0.5.13.5 builtin printf =="
printf '%2$s %1$s\n' A B
echo "[exit status $?]"
""")
run("""
bash show.sh
dash show-dash.sh
""") Output
$ bash show.sh == bash 5.3.20 builtin printf == show.sh: line 3: printf: `$': invalid format character [exit status 1] == /usr/bin/printf on macOS == B A [exit status 0] $ dash show-dash.sh == dash 0.5.13.5 builtin printf == show-dash.sh: 3: printf: %2$: invalid directive [exit status 2]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 printf_numbered_session.py
The bash builtin printed invalid format character and ended with status 1, and the dash builtin printed
invalid directive and ended with status 2. /usr/bin/printf on macOS printed B A with status 0, which is what the
Issue 8 printf page asks for: a conversion that starts with %, a number and a $ takes the argument with that
number.
Builtin versus utility
A shell’s builtin printf and /usr/bin/printf are separate programs that read the same format. For numbered
conversions today, call the external printf by its path, as the session does, and try it on each platform you
target.
Checkpoint
Check: Utility additions in Issue 8
Four of the seven features already run in dash
The table sets what each edition requires beside what the recorded probes printed. Four features are new in Issue 8 and already run in dash 0.5.13.5, so a POSIX sh script may rely on them wherever that dash release or a newer one runs it; three are still missing there, and numbered printf is missing from the bash builtin as well. Answer the two questions under the table to see which rows your own script may use:
Answer the questions above: the table then marks what fits your case and says why the rest does not.
| Feature | POSIX 2017 | POSIX 2024 | bash 5.3.20 | dash 0.5.13.5 |
|---|---|---|---|---|
| $'...' quoting What it adds: escapes such as \t and \n inside $'...' | Poor not defined by Issue 7 | Good section 2.2.4 Dollar-Single-Quotes defines it | Good ran in the probe and expanded the tab | Good ran in the probe and expanded the tab |
| set -o pipefail What it adds: the rightmost failing stage sets the pipeline status | Poor absent from the Issue 7 set builtin | Good added by Austin Group Defect 789 | Good false | true returned 1 in the probe | Good false | true returned 1 in the probe |
| trap -p EXIT What it adds: prints the action of each named condition for reinput | Poor trap had no -p option in Issue 7 | Good added by Austin Group Defects 1211 and 1212 | Good trap -p EXIT printed the action | Poor trap -p EXIT printed nothing |
| read -d delim What it adds: read stops at delim instead of newline | Poor read had only -r in Issue 7 | Good added by Austin Group Defect 243 | Good read -d : stopped at the colon | Poor the probe failed: read -d is not in dash yet |
| test -nt and -ot What it adds: compare the modification times of two files | Poor not a primary of the Issue 7 test utility | Good added by Austin Group Defect 375 | Good f1 -nt f2 and f2 -ot f1 were both true | Good f1 -nt f2 and f2 -ot f1 were both true |
| test -ef What it adds: true when two paths name the same file | Poor not a primary of the Issue 7 test utility | Good added by Austin Group Defect 375 | Good test f1 -ef f1 returned true | Good test f1 -ef f1 returned true |
| %1$s and %2$s What it adds: printf picks the argument by position in the format | Poor no numbered conversions in Issue 7 | Good added by Austin Group Defect 1592 | Poor builtin printed "invalid format character" | Poor its builtin said "invalid directive" |
How the answers decide
- “Does your script name Bash on its first line, or does it have to be plain POSIX sh?” plain POSIX sh: rules out trap -p EXIT, read -d delim and %1$s and %2$s. dash 0.5.13.5 failed the trap -p, read -d and numbered printf probes, so a plain sh script cannot rely on them even on the newest dash it was tested with.
- “Does your script name Bash on its first line, or does it have to be plain POSIX sh?” plain POSIX sh and “Must the script also run under a /bin/sh older than dash 0.5.13.5, such as the dash of Ubuntu 24.04?” Yes, older sh too: adds a caveat to $'...' quoting, set -o pipefail, test -nt and -ot and test -ef. The probes ran dash 0.5.13.5 only, and the manual of Ubuntu 24.04's dash 0.5.12 lists no pipefail option, so probe each feature on the oldest shell before the script relies on it.
- “Does your script name Bash on its first line, or does it have to be plain POSIX sh?” Bash, named on the shebang: rules out %1$s and %2$s. The bash 5.3.20 printf builtin refused the numbered conversion with "invalid format character"; call the external printf utility instead, after trying it on each platform.
Every row reads poor under POSIX 2017 and good under POSIX 2024: each feature is new in Issue 8, so a shell
built for the older edition may lack it and still conform to that edition. The bash and dash cells are what the
probes recorded, not what the shells promise.
For a Debian package, the POSIX 2017 column is what the policy allows a /bin/sh script to assume, while the dash
column is what dash 0.5.13.5 runs. ShellCheck lags in the same way: its SC3003 page says the rule was last applicable
to POSIX.1-2017, and ShellCheck 0.11.0 still flagged $'...' in an sh script in the
POSIX sh versus Bash session, although Issue 8 defines the form and dash
0.5.13.5 runs it.
Checkpoint
Check: What dash 0.5.13.5 runs of Issue 8
A subshell probe tests an option before the script sets it
The pattern is short: the script asks the running shell whether it has a feature, and turns the feature on only when the answer is yes. Step through it for pipefail:
The drawing shows the whole scene; its 4 steps are listed below.
Every step as text
- Probe in a subshell. The parentheses start a child shell. Setting pipefail there changes only the child, so a shell that does not know the option fails without touching the outer script, and 2>/dev/null hides the error message it would print.
- Read the probe status. The exit status of the subshell is the exit status of its last command. If the shell accepted set -o pipefail, the subshell ended with 0; a shell that rejected it ended non-zero and left the outer options as they were.
- Set it when the probe passed. When the status is 0 the main script runs set -o pipefail itself. From then on false | true returns 1, the status of its rightmost failing stage, and a script running under set -e stops there.
- Otherwise, print a note. When the probe failed, the script skips the set stage along the branch, prints a note and goes on without pipefail. A script that cannot run safely without the option stops here instead, with a message that names it.
The probe runs in a subshell because a rejected option can end a script. set is one of the special built-ins, and
the standard’s table of shell errors says a non-interactive shell exits on an error in one of them, so a bare
set -o pipefail on a shell without the option would stop the script on that line; the Bash manual lists the same
rule for Bash in POSIX mode. Inside the parentheses only the child shell ends, and its exit status, the status of its
last command, answers the question. In the recorded session both bash 5.3.20 and dash 0.5.13.5 took the
pipefail enabled branch and printed pipeline status: 1 for the failing pipeline:
from session import run, write
# A POSIX sh script that only switches on pipefail after a probe shell has shown that the current shell supports it.
# The probe runs in a subshell so a failure does not leak into the script's own options. The main script then prints
# the exit status of a failing pipeline, which is 1 only when pipefail took effect. Recorded under GNU Bash 5.3.20
# and dash 0.5.13.5: the probe passes on both, so both runs end with status 1.
write("guard.sh", r"""#!/bin/sh
# probe pipefail in a child shell; the current shell's options stay as they are
if (set -o pipefail) 2>/dev/null; then
set -o pipefail
echo "pipefail enabled"
else
echo "pipefail is not supported here"
fi
# a pipeline whose first stage fails; without pipefail its status would be 0
set +e
false | true
echo "pipeline status: $?"
""")
run("""
chmod +x guard.sh
# POSIX sh is dash on Ubuntu; pipefail reached POSIX in 2024 and dash 0.5.13.5 supports it
dash guard.sh
# the same script under bash 5.3.20
bash guard.sh
# a shell that lacks pipefail would take the else branch; the pipeline status would then be 0
bash -c 'if (set -o pipefail) 2>/dev/null; then echo supports; else echo missing; fi'
""") Output
$ chmod +x guard.sh # POSIX sh is dash on Ubuntu; pipefail reached POSIX in 2024 and dash 0.5.13.5 supports it $ dash guard.sh pipefail enabled pipeline status: 1 # the same script under bash 5.3.20 $ bash guard.sh pipefail enabled pipeline status: 1 # a shell that lacks pipefail would take the else branch; the pipeline status would then be 0 $ bash -c 'if (set -o pipefail) 2>/dev/null; then echo supports; else echo missing; fi' supports
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 pipefail_guard_session.py
The same shape works for every Issue 8 feature a script wants: (trap -p EXIT) >/dev/null 2>&1, a one-shot
printf '%1$s\n' x whose output is compared with x, or a read -r -d : that reads a:b and checks that it got
a. When the script needs the feature, the else branch stops with a message that names it; when the feature is only
nice to have, the else branch sets a flag that the rest of the script reads before it uses the feature.
The tools lag as well. ShellCheck 0.11.0 still reports $'...' in sh scripts as SC3003. Keep that warning while
the script must run on older shells, and silence it with a # shellcheck disable=SC3003 line above the one line that
uses the feature once every shell you target has it. The Debian Policy Manual also names checkbashisms, from the
devscripts package, as a second checker for /bin/sh scripts.
One feature, one guard
dash 0.5.13.5 already runs four of the seven probed features and still lacks three, so give each feature its own guard next to the lines that use it. The script then gains features one at a time as the shells you target catch up.
Checkpoint
Check: Probing before using an Issue 8 feature
Practise: Issue 8, probes and portability
These questions mix the editions with the recorded probe results, and every wrong choice says what it misses. Answer from the sessions on this page rather than from memory:
Practice that adapts to you
Practise this lesson
17 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher to 2–5 years): name four shell-language changes that POSIX.1-2024 added, and one utility change.
POSIX.1-2024, Issue 8 of The Open Group Base Specifications, made four shell-language changes that scripts meet
first: $'...' quoting with backslash escapes (section 2.2.4), the pipefail option of set (Austin Group Defect
789), which gives a pipeline the status of its rightmost failing stage, a Job Control section (2.11), and trap -p,
which prints the actions of the conditions it names in a form the shell can read back. One utility change is
read -d delim, which ends a record at a chosen single byte instead of a newline. In the recorded probes dash 0.5.13.5
ran $'...' and pipefail, while trap -p and read -d still printed unsupported there, so a /bin/sh script
needs a probe before it uses either.
Two harder questions start where this lesson stops, on images whose dash lacks a feature the team wanted. Sketch the commands you would try on such an image before you open each question’s first step:
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Fresher to staff
A
/bin/shbackup job runsdump_orders | gzip > orders.gz. The oldest image's dash has no pipefail, so the guard leaves it off there. When the dump fails half-way, the job still ends with status 0 and a valid-looking archive. How do you catch the failed first stage without pipefail, and keep only complete archives? -
Fresher to staff
A shared POSIX sh library function sets INT and EXIT traps while it holds a lock, and must give the caller's traps back when it returns. The oldest images run dash 0.5.13.5 as
/bin/sh, which has notrap -p. How do you save and restore the caller's traps there, and show that the restore is exact?
Flashcards · 6 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
What is POSIX.1-2024, and how does it relate to the previous edition?
Issue 8 of The Open Group Base Specifications. It mostly adds to Issue 7, POSIX.1-2017, and its few removals, such as test's -a and -o, had been marked obsolescent first.
-
Which four shell-language changes of POSIX.1-2024 do scripts meet first?
$'...' quoting with backslash escapes, the pipefail option of set, a Job Control section, and trap -p, which prints the current trap actions.
-
Which utility changes of POSIX.1-2024 reach shell scripts?
read -d, the test operators -nt, -ot and -ef, numbered printf conversions such as %1$s, and new utilities, among them realpath, readlink and timeout.
-
How many of the seven probed Issue 8 features did bash 5.3.20 and dash 0.5.13.5 run?
bash ran six and refused %1$s in its printf builtin; dash ran four and refused trap -p, read -d and %1$s.
-
Does a feature's place in POSIX.1-2024 make it safe in a #!/bin/sh script?
Not yet: Debian policy still lets sh scripts assume only POSIX.1-2017 plus named extras, and ShellCheck still flags $'...' in sh, so check that the running shell has the feature.
-
How does a script test for an option such as pipefail before it relies on it?
Probe it in a subshell, as in (set -o pipefail) 2>/dev/null, turn it on only when the probe passes, and give the other branch a clear message or a flag.
Key takeaways
- POSIX.1-2024 is Issue 8 of The Open Group Base Specifications; it mostly adds to Issue 7 (POSIX.1-2017), and its few
removals, such as the
-aand-ooperators oftest, had been marked obsolescent first. - The four shell-language changes scripts meet first are
$'...'quoting,set -o pipefail, the Job Control section (2.11) andtrap -p, and the first two already run in both bash 5.3.20 and dash 0.5.13.5. - The utilities gained
read -d, thetestoperators-nt,-otand-efand numberedprintfconversions, and new programs such asrealpath,readlinkandtimeoutarrived. - Of the seven probed features, bash 5.3.20 ran six and refused
%1$sin its printf builtin, while dash 0.5.13.5 ran four and refusedtrap -p,read -dand%1$s. - Debian policy still lets
/bin/shscripts assume only POSIX.1-2017 plus named extras, and ShellCheck still flags$'...'insh, so a feature’s place in Issue 8 does not make it portable yet. - Probe each feature in a subshell, such as
(set -o pipefail) 2>/dev/null, turn it on only when the probe passes, and give the else branch either a clear message or a flag.
Terms in this lesson
- Issue 7
- The seventh edition of The Open Group Base Specifications, aligned with IEEE Std 1003.1-2008 and later corrigenda (POSIX.1-2017). Debian policy still names this edition as the baseline for /bin/sh scripts.
- Issue 8
- The eighth edition of The Open Group Base Specifications, published jointly as IEEE Std 1003.1-2024 (POSIX.1-2024). It replaces Issue 7 and adds dollar-single-quotes, pipefail, trap -p, a Job Control section (2.11) and new behaviour to read, test and printf.
References
- The Open Group Base Specifications Issue 8 (The Open Group and IEEE)
- POSIX.1-2024: Shell Command Language (The Open Group and IEEE)
- POSIX.1-2024: set (The Open Group and IEEE)
- POSIX.1-2024: trap (The Open Group and IEEE)
- POSIX.1-2024: echo (The Open Group and IEEE)
- POSIX.1-2024: read (The Open Group and IEEE)
- POSIX.1-2024: test (The Open Group and IEEE)
- POSIX.1-2024: printf (The Open Group and IEEE)
- POSIX.1-2024: realpath (The Open Group and IEEE)
- POSIX.1-2024: readlink (The Open Group and IEEE)
- POSIX.1-2024: timeout (The Open Group and IEEE)
- POSIX.1-2024: find (The Open Group and IEEE)
- POSIX.1-2024: xargs (The Open Group and IEEE)
- POSIX.1-2024: sed (The Open Group and IEEE)
- The Open Group Base Specifications Issue 7, 2018 edition (The Open Group and IEEE)
- POSIX.1-2017: Shell Command Language (The Open Group and IEEE)
- Bash Reference Manual: The Set Builtin (Chet Ramey (GNU Bash maintainer))
- Bash Reference Manual: Bash POSIX Mode (Chet Ramey (GNU Bash maintainer))
- dash(1): command interpreter (shell) (Linux man-pages project)
- Ubuntu 24.04 manual page: dash(1) (Canonical (Ubuntu))
- Ubuntu 24.04 package dash: list of files (Canonical (Ubuntu))
- Debian Policy Manual: Scripts (The Debian Project)
- ShellCheck SC3003: dollar-single-quotes (ShellCheck project)
Report a problem with this lesson
Kept only in this browser. Your Learn progress