Skip to content

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

Open
iav wants to merge 10 commits into
mainfrom
fix/p2a-for-loop-expansion
Open

(#9400 P2a) Replace echo|tr/sed subshells with parameter expansion#9809
iav wants to merge 10 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. Bug pointed out by coderabbitai on this PR.

Out of scope (deferred)

Style commits

Three commits in the middle of the chain are pure shfmt whitespace fixes on the three files this PR touches (extensions.sh, interactive.sh, distro-agnostic.sh) — keeping them on these files only, so the diff stays scoped. They contain no behavior changes.

Test plan

  • bash lib/tools/shellcheck.sh — clean, no new warnings on the changed files (/tmp/.overlayfs_wrapper_* reads, comma-split loops, countdowns)
  • shfmt — files in this PR conform to project config

Summary by CodeRabbit

  • Chores
    • Refactored internal scripts for more efficient list handling and iteration, reducing subprocess use.
    • Improved cleanup/unmount and extension-loading flows for more reliable operation.
    • Reformatted configuration, countdown, bootscript and rootfs logic for clearer parsing and maintainability while preserving behavior.

Review Change Stack

iav added 9 commits May 11, 2026 15:49
…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.

Behavior is preserved as-is; the pre-existing 'ttyS0' literal-quotes default
is intentionally left untouched (out of scope for P2a).

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
Whitespace-only changes to bring the file in line with the project's shfmt
config (space_redirects=true). Touched here because the file was modified in
this branch; running shfmt on it would have produced these whitespace deltas
anyway.

Assisted-by: Claude:claude-opus-4.7
Whitespace-only changes to bring the file in line with the project's
.editorconfig (tabs, indent-size 4). The file had drifted into 4-space indent
in two functions. Touched here because the file was modified in this branch.

Assisted-by: Claude:claude-opus-4.7
Whitespace-only changes to bring the file in line with the project's shfmt
config (space_redirects=true, switch_case_indent=true, no space before ';').
Touched here because the file was modified in this branch.

Assisted-by: Claude:claude-opus-4.7
@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

This PR refactors multiple shell scripts to replace external command pipelines and subshells with native Bash constructs: seq → arithmetic loops, echo | tr → parameter expansion, sed → parameter expansion, and command substitution → mapfile arrays.

Changes

Shell Syntax Modernization

Layer / File(s) Summary
Countdown Arithmetic Loops
lib/functions/general/countdown.sh
Replace seq enumeration with for ((i = 1; i <= loops; i++)) in exit_if_countdown_not_aborted and countdown_and_continue_if_not_aborted.
Extension Manager String Splitting
lib/functions/general/extensions.sh
Auto-extension loading uses ${ENABLE_EXTENSIONS//,/ } instead of echo ... | tr; extension file selection uses mapfile with null-safe find/xargs/grep to pick files containing hook-prefixed functions.
Overlay Filesystem Cleanup Arrays
lib/functions/compilation/utils-compilation.sh
Read overlay unmount and cleanup lists into arrays via mapfile -t and iterate arrays for umount -l and removal.
Build Package Clean Level Iteration
lib/functions/main/build-packages.sh
Cleaning fragment loop uses ${CLEAN_LEVEL//,/ } parameter expansion instead of tr comma-to-space conversion.
Interactive Kernel Configuration
lib/functions/configuration/interactive.sh
Whitespace/formatting in get_kernel_info_for_branch; interactive_config_ask_branch now splits KERNEL_TARGET with ${KERNEL_TARGET//,/ } instead of echo ... | tr.
Bootscript and Console Parameter Handling
lib/functions/rootfs/distro-agnostic.sh
Reformatted console parameter loops; condensed render_bootscript_template terminator; BOOTSCRIPT .template handling layout adjusted; SERIALCON parsed with ${_serialcon_csv//,/ } and default ttyS0.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 Pipelines retire, Bash shines bright,
Parameter expansion sets things right,
No more tr, no more seq,
Native loops and arrays skip the leak,
Small hops, fewer subs, tidy night!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% 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 title accurately summarizes the main technical change: replacing echo|tr/sed subshells with bash parameter expansion, which is the primary focus across all modified 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
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
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/medium PR with more then 50 and less then 250 lines

Development

Successfully merging this pull request may close these issues.

1 participant