Changing memory_map.c to memory_mapping.c affects a directory entry. Replacing memory_map inside C source changes file data. They may belong to one refactor, but they have different failure modes and should never be hidden inside one clever pipeline.

Quick answer for one filename

Terminalbash
old='memory_map.c'
new=${old/map/mapping}
printf '%q -> %q\n' "$old" "$new"

What the preview does

  • ${old/map/mapping} is Bash parameter expansion: it replaces the first literal map in the variable value. It does not alter the file.

  • printf %q produces shell-escaped output, making spaces and control characters easier to notice during review.

  • The match is case-sensitive and applies anywhere in the basename; add stricter validation when only a prefix, suffix, or extension should change.

  • Variables are quoted when passed to commands so whitespace and wildcard characters remain part of one filename.

Terminalbash
old='memory_map.c'
new=${old/map/mapping}
[[ -e $old || -L $old ]] || { printf 'Missing source: %q\n' "$old" >&2; exit 66; }
[[ ! -e $new && ! -L $new ]] || { printf 'Destination exists: %q\n' "$new" >&2; exit 73; }
mv -- "$old" "$new"

Risk level: caution. Review the command before running it.

Why this rename is guarded

  • -e detects an existing target; -L also detects a dangling symbolic link, which must not be silently replaced.

  • The destination check fails before mv, avoiding accidental overwrite. This is clearer than assuming every platform gives identical mv -n status semantics.

  • mv -- ends option parsing, so a filename beginning with - is treated as data.

  • Within one filesystem, mv normally renames a directory entry. Across filesystems it can copy then remove the source, changing atomicity and failure behavior.

  • Exit 66 identifies a missing input and 73 an output conflict for automation; any nonzero mv result still stops a shell using set -e.

Do not assume every rename command is the same

Linux distributions commonly expose one of two incompatible programs as rename: util-linux uses substring replacement file..., while Perl rename uses a Perl expression such as s/map/mapping/. A command written for one can fail—or mean something else—on the other.

Terminalbash
command -V rename
rename --version 2>&1 | head -n 2
rename --help 2>&1 | sed -n '1,25p'

Identification takeaways

  • command -V reveals aliases, functions, built-ins, and executable resolution better than assuming /usr/bin/rename.

  • Version/help output identifies the implementation and supported safety flags. Read the local help before copying syntax.

  • Do not install a second implementation merely to follow an example; a portable guarded loop is easier to audit on mixed fleets.

Bulk rename with a portable Bash plan

The safest general pattern builds an explicit old-to-new plan, checks for duplicate or pre-existing destinations, and only then performs changes. The following script handles spaces and newlines because it does not parse ls output.

rename-literal-part.shbash
#!/usr/bin/env bash
set -euo pipefail
 
mode=${1:-}
search=${2:?"usage: rename-literal-part.sh [--apply] SEARCH REPLACEMENT FILE..."}
replacement=${3-}
shift 3
(($# > 0)) || { printf "No files supplied\n" >&2; exit 64; }
[[ -n $search ]] || { printf "SEARCH must not be empty\n" >&2; exit 64; }
 
declare -A planned=()
for old do
  dir=${old%/*}; [[ $dir != "$old" ]] || dir=.
  base=${old##*/}
  newbase=${base/"$search"/"$replacement"}
  new=$dir/$newbase
 
  [[ $newbase != "$base" ]] || { printf "SKIP no match: %q\n" "$old"; continue; }
  [[ -e $old || -L $old ]] || { printf "ERROR missing: %q\n" "$old" >&2; exit 66; }
  [[ ! -e $new && ! -L $new ]] || { printf "ERROR destination exists: %q\n" "$new" >&2; exit 73; }
  [[ ! ${planned[$new]+yes} ]] || { printf "ERROR duplicate destination: %q\n" "$new" >&2; exit 73; }
  planned[$new]=$old
  printf "%q -> %q\n" "$old" "$new"
  [[ $mode == --apply ]] && mv -- "$old" "$new"
done

Preview by default; pass --apply only after reviewing every mapping.

What the script protects

  • Bash arrays preserve argument boundaries, including embedded whitespace and newlines; never feed filenames by splitting ls output.

  • Only the basename is transformed. The directory prefix is preserved rather than accidentally renaming path components.

  • ${base/"$search"/"$replacement"} performs one literal replacement, not a regular expression. Use ${base//.../...} only after deciding that every occurrence should change.

  • The associative array rejects two sources mapping to the same destination during one run. Existing files and dangling symlinks are rejected separately.

  • Dry-run is the default. --apply is explicit, but the loop is not transactional: an error after earlier moves can leave a partially renamed set. Use version control, backups, or a reversible manifest.

Preview selected C files

Terminalbash
./rename-literal-part.sh --preview map mapping -- ./*.c

Selection takeaways

  • The shell expands ./*.c before the script runs. With Bash’s default settings, an unmatched glob may remain literal and then be reported missing.

  • The explicit file list limits scope to the current directory and .c suffix; it does not recurse.

  • A leading ./ keeps option-looking basenames unambiguous, while the script also passes -- to mv.

  • Review skipped names, conflicts, case, extensions, and surprising multiple matches before using --apply.

Apply the reviewed set

Terminalbash
./rename-literal-part.sh --apply map mapping -- ./*.c

Risk level: caution. Review the command before running it.

Apply-stage takeaways

  • Run this only when the preview was generated from the same directory state; another process can create a race between check and move.

  • The script prints a manifest as it works. Capture it in a controlled log if rollback must be reconstructed.

  • Git users should inspect git status --short and git diff --summary; Git infers renames from content rather than storing a special rename operation.

  • Renaming a source file does not update build manifests, imports, include directives, links, case-sensitive references, or generated files.

Using util-linux rename safely

If local help confirms the util-linux implementation, its modern interface accepts literal substring and replacement operands. Upstream explicitly warns that unguarded operation has no safety net.

Terminalbash
rename --no-act --verbose --no-overwrite map mapping -- ./*.c

util-linux preview takeaways

  • --no-act previews and --verbose reports mappings; --no-overwrite protects existing destinations if the apply command uses the same option.

  • The default replaces the first substring occurrence. --all changes every occurrence and can produce unexpected names.

  • Support for these flags is version-dependent; the local --help output is authoritative.

  • A preview does not lock the directory. Recheck immediately before applying in shared or generated trees.

Using Perl rename safely

If the installed tool identifies itself as Perl rename, the transformation is a Perl expression. This is powerful enough to change anchors, capture groups, case, and extensions—and powerful enough to overmatch.

Terminalbash
rename --nono --verbose 's/map/mapping/' -- ./*.c

Perl rename preview takeaways

  • On Debian’s Perl rename, --nono (often -n) previews and --verbose shows candidate renames. Confirm options in local help.

  • s/map/mapping/ is a regular-expression substitution. Metacharacters in user data must be escaped; this is not a general literal-string API.

  • Without the g modifier only the first match changes. Anchors such as ^ and $ are safer when the intended position is known.

  • Never construct and evaluate a Perl expression from untrusted strings. Use the guarded literal script or a language API instead.

Replace text inside files as a separate operation

First ask whether the text is an identifier, configuration value, prose string, generated artifact, or binary data. A language-aware refactoring tool understands symbols and references better than sed; textual replacement is appropriate only when a literal textual match is truly intended.

Find and review text matches

Terminalbash
rg --line-number --fixed-strings --glob '*.c' --glob '*.h' 'memory_map' .

Search takeaways

  • --fixed-strings treats the needle literally instead of as a regular expression.

  • The two --glob filters constrain eligible filenames; ripgrep also honors ignore files by default. Use --hidden or --no-ignore only after reviewing the broader scope.

  • Line-numbered output is for human review, not safe filename parsing. A filename can contain colons, spaces, or newlines.

  • Confirm comments, strings, generated code, vendored dependencies, tests, and public API compatibility before replacing all matches.

Preview transformed content without writing

Terminalbash
sed 's/memory_map/memory_mapping/g' -- src/memory_mapping.c | diff -u -- src/memory_mapping.c - || true

Content-preview takeaways

  • Without -i, sed writes transformed text to standard output and leaves the file unchanged.

  • diff -u original - compares the file with transformed standard input; || true prevents expected “files differ” status 1 from aborting an interactive shell.

  • The sed pattern is a basic regular expression. Escape metacharacters or choose a different delimiter when search/replacement contains /.

  • This preview is for a text file. Do not run generic stream replacements over binaries or unknown encodings.

Edit one reviewed file with a backup

Terminalbash
sed --in-place=.before-memory-rename \
  's/memory_map/memory_mapping/g' \
  -- src/memory_mapping.c
diff -u -- src/memory_mapping.c.before-memory-rename src/memory_mapping.c || true

Risk level: caution. Review the command before running it.

In-place edit takeaways

  • GNU sed --in-place=.before-memory-rename writes a temporary result, then renames it into place while retaining the original under the suffix. Without a suffix, no backup is retained.

  • In-place replacement can change inode identity and interact with symlinks, hard links, permissions, labels, watchers, and concurrent writers. Test on the target filesystem.

  • The backup contains source data and possibly secrets; retain and dispose of it through normal policy.

  • Run formatters, compilers, tests, and semantic review after textual refactors. A syntactically valid replacement can still change APIs or behavior.

Handle many matching text files without broken filenames

Terminalbash
rg --files-with-matches --null --fixed-strings \
  --glob '*.c' --glob '*.h' 'memory_map' . \
| xargs -0 --no-run-if-empty sed --in-place=.before-memory-rename \
    's/memory_map/memory_mapping/g'

Risk level: caution. Review the command before running it.

Pipeline safety notes

  • --null and xargs -0 preserve every filename byte except NUL, including whitespace and newlines.

  • --no-run-if-empty prevents GNU xargs from invoking sed with no files. This option is not portable to every Unix implementation.

  • The pipeline still has a time-of-check/time-of-use window and can partially complete. Avoid concurrent writers and capture the exact changed set.

  • Every selected file receives a backup suffix, even on some implementations when content does not change; check disk capacity and clean up only after validation.

  • For repositories, a small language script or refactoring tool can provide better error handling, encoding rules, collision checks, and a machine-readable manifest.

Rollback and verification

Terminalbash
git status --short
git diff --stat
git diff --check
rg --line-number --fixed-strings 'memory_map' --glob '*.c' --glob '*.h' . || true

Verification takeaways

  • git status and git diff show tracked changes; untracked backup files still need deliberate handling.

  • git diff --check detects whitespace errors, not semantic correctness.

  • A remaining-name search finds missed textual references but cannot prove that every intended symbol was safely renamed.

  • Build and test the affected components, inspect public interfaces, and verify packaging/deployment paths before deleting backups.

Common failure patterns

  • `rename` reports syntax errors: the example targets the other rename implementation; inspect version/help.

  • Two names collapse into one: the replacement is not one-to-one; stop and resolve the collision rather than overwriting.

  • Spaces/newlines break the batch: filenames were parsed as lines/words; use arrays or NUL-delimited interfaces.

  • Hidden files were missed: ordinary globs omit dotfiles; expand scope deliberately rather than enabling everything by reflex.

  • Only filenames changed: update build files, imports/includes, scripts, links, documentation, and external consumers as needed.

  • Text in binaries was damaged: restrict processing to known text formats and use format-aware tools.

  • Case-only rename is ignored elsewhere: case-insensitive filesystems and Git workflows may require an intermediate name and cross-platform validation.

  • Permissions or ownership changed: in-place editors create replacement files; validate metadata and security labels on sensitive paths.

  • Partial batch after error: operations were not transactional; use the manifest/backups/version control to reverse completed steps.

Primary references

  • The util-linux project documents `rename(1)`, its substring syntax, no-act/no-overwrite controls, warnings, and exit statuses.

  • GNU Coreutils documents `mv`, overwrite controls, cross-filesystem fallback, and exit semantics.

  • The GNU sed manual explains in-place editing and backup suffixes, regular expressions, and NUL-oriented processing.

  • GNU Coreutils explains `--no-target-directory` for avoiding ambiguous destination-directory interpretation.