Skip to content

(#9400 P2a) Replace echo|tr/sed subshells with parameter expansion#9809

Draft
iav wants to merge 7 commits into
mainfrom
fix/p2a-for-loop-expansion
Draft

(#9400 P2a) Replace echo|tr/sed subshells with parameter expansion#9809
iav wants to merge 7 commits into
mainfrom
fix/p2a-for-loop-expansion

Conversation

@iav
Copy link
Copy Markdown
Contributor

@iav iav commented May 11, 2026

Summary

Replaces 6 instances where a for loop iterates over a comma-separated string by spawning echo … | tr/sed (or tr <<<) subshells. All replacements use bash-native parameter expansion ${VAR//,/ }, plus one switch from seq(1) to a C-style arithmetic loop and one switch from word-splitting $(< file) to mapfile. Part of the bash-safety cleanup tracked in #9400 (P2a).

GitHub issue reference: #9400
Jira reference number AR-2818

Changes

File Before After
lib/functions/general/extensions.sh for x in $(echo "${ENABLE_EXTENSIONS:-${EXT}}" | tr "," " ") local CSV var + ${_enable_csv//,/ }
lib/functions/configuration/interactive.sh for x in $(echo "${KERNEL_TARGET}" | tr "," "\n") for x in ${KERNEL_TARGET//,/ }
lib/functions/general/countdown.sh (×2) for i in $(seq 1 "${loops}") for ((i = 1; i <= loops; i++))
lib/functions/main/build-packages.sh for x in $(tr ',' ' ' <<< "${CLEAN_LEVEL}") for x in ${CLEAN_LEVEL//,/ }
lib/functions/rootfs/distro-agnostic.sh for i in $(echo "${SERIALCON:-'ttyS0'}" | sed "s/,/ /g") local CSV var + ${_serialcon_csv//,/ }; also drop the literal single quotes from the default (see follow-up commit below)
lib/functions/compilation/utils-compilation.sh (×2) for dir in $(< /tmp/.overlayfs_wrapper_*) mapfile -t arr < file; for dir in "${arr[@]}"

Why this matters

Each echo "${var}" | tr/sed form forks a subshell and one or two external processes just to do what bash can do in-place with ${var//pattern/replacement}. The replacements also reduce one class of word-splitting surprise: in the mapfile case, paths with whitespace are now preserved verbatim instead of being broken apart.

Note on intentional word splitting

The replacements for x in ${VAR//,/ } deliberately keep the expansion unquoted — that is the mechanism for the loop to split on whitespace. Inside [[ ]] or assignments these expansions would still need to be quoted; outside the loop header, they are not modified.

Follow-up: SERIALCON default

A separate commit drops the literal single quotes from the SERIALCON:-'ttyS0' default. When SERIALCON was unset, the value 'ttyS0' (with the quotes) was retained through the parameter expansion and downstream array[0] became the 4-character string 'ttyS0' — producing a malformed /etc/securetty entry and a non-existent serial-getty@'ttyS0'.service path.

Out of scope (deferred)

Summary by CodeRabbit

  • Chores
    • Replaced subprocess-heavy list parsing with safer, faster in-shell split/iteration to reduce overhead.
    • Improved cleanup/unmount and extension-loading flows to iterate tracked entries reliably.
    • Simplified interactive configuration menus, countdown loops, cleaning fragments, and serial-console parsing for clearer parsing and maintainable behavior while preserving existing functionality.

Review Change Stack

@iav iav requested a review from a team as a code owner May 11, 2026 13:10
@iav iav requested review from TRSx80 and hzyitc and removed request for a team May 11, 2026 13:10
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

📝 Walkthrough

Walkthrough

Replace external shell pipelines and command-substitution loops with native Bash constructs: mapfile arrays for cleanup lists, arithmetic for-loops instead of seq, and parameter-expansion splitting for comma-separated values across several scripts.

Changes

Shell Syntax Modernization

Layer / File(s) Summary
Overlay filesystem cleanup arrays
lib/functions/compilation/utils-compilation.sh
Read overlay unmount and cleanup files into arrays via mapfile -t and iterate arrays for umount -l and removal.
Interactive kernel configuration
lib/functions/configuration/interactive.sh
Split KERNEL_TARGET with ${KERNEL_TARGET//,/ } in interactive_config_ask_branch.
Countdown arithmetic loops
lib/functions/general/countdown.sh
Replace for i in $(seq 1 ...) with for ((i = 1; i <= loops; i++)) in countdown functions.
Extension manager splitting and discovery
lib/functions/general/extensions.sh
Split CSV extension lists with ${_enable_csv//,/ } for extension iteration.
Build package clean-level iteration
lib/functions/main/build-packages.sh
Derive cleaning fragments from CLEAN_LEVEL using ${CLEAN_LEVEL//,/ } instead of tr.
Bootscript and console parameter handling
lib/functions/rootfs/distro-agnostic.sh
Parse SERIALCON into a local _serialcon_csv (default ttyS0) and split via ${_serialcon_csv//,/ } when enabling serial getty instances.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • hzyitc
  • igorpecovnik

Poem

🐰 Pipelines trimmed, the shell leaps light,

Arrays collect what once took flight,
Loops count steadily, no subshell drum,
Commas expand, the tokens come,
A tidy warren, clean and bright.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately and specifically summarizes the main change: replacing echo|tr/sed subshells with bash parameter expansion across multiple files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/p2a-for-loop-expansion

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added 05 Milestone: Second quarter release size/medium PR with more then 50 and less then 250 lines Needs review Seeking for review Framework Framework components labels May 11, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/functions/rootfs/distro-agnostic.sh`:
- Around line 509-510: The default value for SERIALCON currently includes
literal single quotes which become part of _serialcon_csv and break downstream
device names; change the assignment of local _serialcon_csv from
"${SERIALCON:-'ttyS0'}" to "${SERIALCON:-ttyS0}" (remove the single quotes) so
that when you split with ${_serialcon_csv//,/ } you get clean device names like
ttyS0 rather than 'ttyS0'; update the variable _serialcon_csv in the code where
it is declared to use the unquoted default.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 74de7416-83c6-49c5-a198-749ae85d2345

📥 Commits

Reviewing files that changed from the base of the PR and between ebe3eeb and 6c9900c.

📒 Files selected for processing (6)
  • lib/functions/compilation/utils-compilation.sh
  • lib/functions/configuration/interactive.sh
  • lib/functions/general/countdown.sh
  • lib/functions/general/extensions.sh
  • lib/functions/main/build-packages.sh
  • lib/functions/rootfs/distro-agnostic.sh

Comment thread lib/functions/rootfs/distro-agnostic.sh Outdated
iav added a commit that referenced this pull request May 11, 2026
When SERIALCON is unset, the default 'ttyS0' (with embedded single quotes)
was retained verbatim through the parameter expansion, so the loop variable
became the 4-character string "'ttyS0'" rather than "ttyS0". Downstream
that produces a malformed `/etc/securetty` entry and a systemd unit path
`serial-getty@'ttyS0'.service` — neither matches a real device, and
`systemctl daemon-reload`/`enable` fails silently.

The original odd default predates this branch but was uncovered after the
P2a rewrite of the loop header surfaced the value directly. Bug pointed out
by coderabbitai on PR #9809.

Assisted-by: Claude:claude-opus-4.7
@iav iav force-pushed the fix/p2a-for-loop-expansion branch from f44ba0d to 863e865 Compare May 20, 2026 00:52
iav added a commit that referenced this pull request May 20, 2026
When SERIALCON is unset, the default 'ttyS0' (with embedded single quotes)
was retained verbatim through the parameter expansion, so the loop variable
became the 4-character string "'ttyS0'" rather than "ttyS0". Downstream
that produces a malformed `/etc/securetty` entry and a systemd unit path
`serial-getty@'ttyS0'.service` — neither matches a real device, and
`systemctl daemon-reload`/`enable` fails silently.

The original odd default predates this branch but was uncovered after the
P2a rewrite of the loop header surfaced the value directly. Bug pointed out
by coderabbitai on PR #9809.

Assisted-by: Claude:claude-opus-4.7
iav added 7 commits May 21, 2026 21:29
…sion

Replace 'for x in $(echo "${VAR}" | tr "," " ")' with the bash-native
'${VAR//,/ }' pattern, avoiding the subshell and pipe.

Part of the bash-safety cleanup in #9400 (P2a).

Assisted-by: Claude:claude-opus-4.7
… parameter expansion

Replace 'for x in $(echo "${KERNEL_TARGET}" | tr "," "\\n")' with the
bash-native '${KERNEL_TARGET//,/ }' pattern, avoiding the subshell and pipe.
The for-loop word-splits on whitespace, so substituting commas with spaces
is equivalent to the original substitution with newlines.

Part of the bash-safety cleanup in #9400 (P2a).

Assisted-by: Claude:claude-opus-4.7
…thmetic for-loop

Replace 'for i in $(seq 1 "${loops}")' with C-style 'for ((i = 1; i <= loops; i++))',
avoiding the seq(1) subshell. 'loops' is already 'declare -i' in both callers.

Part of the bash-safety cleanup in #9400 (P2a).

Assisted-by: Claude:claude-opus-4.7
…expansion

Replace 'for x in $(tr "," " " <<< "${CLEAN_LEVEL}")' with the bash-native
'${CLEAN_LEVEL//,/ }' pattern, avoiding the subshell.

Part of the bash-safety cleanup in #9400 (P2a).

Assisted-by: Claude:claude-opus-4.7
…arameter expansion

Replace 'for i in $(echo "${SERIALCON:-...}" | sed "s/,/ /g")' with the
bash-native '${var//,/ }' pattern, avoiding the subshell and pipe. Use a
local intermediate variable because parameter expansion does not nest cleanly
with the default-value operator.

Part of the bash-safety cleanup in #9400 (P2a).

Assisted-by: Claude:claude-opus-4.7
…dsub with mapfile

Replace 'for dir in $(< /tmp/.overlayfs_wrapper_*)' — which uses unquoted
command substitution and relies on word-splitting — with 'mapfile -t arr < file'
followed by 'for dir in "${arr[@]}"'. The mapfile/array form preserves entries
verbatim (one path per line) and handles paths with whitespace correctly.

Part of the bash-safety cleanup in #9400 (P2a).

Assisted-by: Claude:claude-opus-4.7
When SERIALCON is unset, the default 'ttyS0' (with embedded single quotes)
was retained verbatim through the parameter expansion, so the loop variable
became the 4-character string "'ttyS0'" rather than "ttyS0". Downstream
that produces a malformed `/etc/securetty` entry and a systemd unit path
`serial-getty@'ttyS0'.service` — neither matches a real device, and
`systemctl daemon-reload`/`enable` fails silently.

The odd default predates this branch but was uncovered after the
P2a rewrite of the loop header surfaced the value directly.

Assisted-by: Claude:claude-opus-4.7
@iav iav force-pushed the fix/p2a-for-loop-expansion branch from 863e865 to 1ebe719 Compare May 21, 2026 19:05
@github-actions github-actions Bot added size/small PR with less then 50 lines and removed size/medium PR with more then 50 and less then 250 lines labels May 21, 2026
@iav iav marked this pull request as draft May 25, 2026 02:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

05 Milestone: Second quarter release Framework Framework components Needs review Seeking for review size/small PR with less then 50 lines

Development

Successfully merging this pull request may close these issues.

1 participant