Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/bash/file/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ update_file_section ~/.bash_profile "# BEGIN APP" "# END APP" \
## Behavior Notes

- Returns success when the target file does not exist and there is nothing to remove.
- Replaces only the first matching marked section when markers already exist.
- Replaces or removes only the first matching marked section when markers already exist.
- Treats markers as exact full lines; marker text embedded in longer lines is ignored.
- Appends the marked block when markers are not present.

## Tests
Expand Down
23 changes: 15 additions & 8 deletions lib/bash/file/lib_file.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ __file_section_markers_ordered__() {
#
# This function can add, update, or remove a section of text in a file.
# It is designed to be safe to run multiple times. If the section already
# exists, it will be replaced. If it doesn't exist, it will be appended.
# If the target file itself does not exist, this returns success without
# creating the file.
# exists, the first full-line marker pair will be replaced. If it doesn't
# exist, it will be appended. If the target file itself does not exist, this
# returns success without creating the file.
#
# Usage:
# update_file_section [options] <target_file> <start_marker> <end_marker> [content_lines...]
Expand Down Expand Up @@ -133,8 +133,8 @@ update_file_section() {
fi

local beginning_marker_count end_marker_count
beginning_marker_count=$(grep -cF -- "$beginning_marker" "$target_file" || true)
end_marker_count=$(grep -cF -- "$end_marker" "$target_file" || true)
beginning_marker_count=$(grep -cxF -- "$beginning_marker" "$target_file" || true)
end_marker_count=$(grep -cxF -- "$end_marker" "$target_file" || true)
if ((beginning_marker_count != end_marker_count)); then
log_error "Asymmetric markers in '$target_file': $beginning_marker_count start, $end_marker_count end. Manual repair needed."
return 1
Expand Down Expand Up @@ -232,9 +232,16 @@ update_file_section() {
if [[ "$section_exists" == true ]]; then
if [[ "$remove_section" == true ]]; then
if awk -v START_M="$beginning_marker" -v END_M="$end_marker" '
BEGIN { in_section = 0 }
$0 == START_M { in_section = 1; next }
$0 == END_M { in_section = 0; next }
BEGIN {
in_section = 0
processed = 0
}
$0 == START_M && processed == 0 { in_section = 1; next }
$0 == END_M && in_section == 1 {
in_section = 0
processed = 1
next
}
{
if (in_section == 0) {
print $0
Expand Down
34 changes: 34 additions & 0 deletions lib/bash/file/tests/lib_file.bats
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,21 @@ EOF
[ "$(cat "$target")" = $'before\n# BEGIN\nnew\n# END\nafter' ]
}

@test "update_file_section ignores marker substrings embedded in longer lines" {
local target="$TEST_TMPDIR/config.txt"
cat <<'EOF' > "$target"
before
echo # BEGIN
old
echo # END
after
EOF

update_file_section "$target" "# BEGIN" "# END" "new"

[ "$(cat "$target")" = $'before\necho # BEGIN\nold\necho # END\nafter\n# BEGIN\nnew\n# END' ]
}

@test "update_file_section preserves executable file mode when replacing" {
local target="$TEST_TMPDIR/script.sh"
cat <<'EOF' > "$target"
Expand Down Expand Up @@ -201,6 +216,25 @@ EOF
[ "$(cat "$target")" = $'before\nafter' ]
}

@test "update_file_section removes only the first matching marked block with -r" {
local target="$TEST_TMPDIR/config.txt"
cat <<'EOF' > "$target"
before
# BEGIN
remove-me
# END
middle
# BEGIN
keep-me
# END
after
EOF

update_file_section -r "$target" "# BEGIN" "# END"

[ "$(cat "$target")" = $'before\nmiddle\n# BEGIN\nkeep-me\n# END\nafter' ]
}

@test "update_file_section rejects a section with only a start marker" {
local target="$TEST_TMPDIR/config.txt"
cat <<'EOF' > "$target"
Expand Down
Loading