Skip to content

Instantly share code, notes, and snippets.

@cjgunase
Last active June 3, 2025 18:40
Show Gist options
  • Save cjgunase/8d49547e4dc1dd4a40e36ab36c7bf670 to your computer and use it in GitHub Desktop.
Save cjgunase/8d49547e4dc1dd4a40e36ab36c7bf670 to your computer and use it in GitHub Desktop.
eusable renamer that works for any set of files whose names contain a token you want to swap for a value taken from a mapping table.
#!/usr/bin/env bash
###############################################################################
# rename_by_map.sh
#
# Rename every file whose name contains a “key” token that appears in a
# two‑column mapping file. The token is replaced by its corresponding value.
#
# Features
# ─────────────────────────────────────────────────────────────────────────────
# • Dry‑run by default (prints the mv commands).
# • `--apply` flag actually performs the renames.
# • Works in any directory, on any file type (you can restrict with --glob).
# • Handles tabs OR spaces in the mapping file.
# • Strips hidden Windows carriage‑returns automatically.
# • Warns when a key in a filename is missing from the map.
#
# Usage examples
# ─────────────────────────────────────────────────────────────────────────────
# # 1) Rename *.cov files using ind‑to‑GTEx map (classic case)
# ./rename_by_map.sh -m ind_map.txt -g "Black_*.cov" --apply
#
# # 2) Rename pictures: IMG0001.jpg → Vacation_0001.jpg
# ./rename_by_map.sh -m pic_map.txt -g "*.jpg"
#
# # 3) Preview what would happen in /data/run42 (no --apply)
# ./rename_by_map.sh -d /data/run42 -m barcode_map.tsv
#
###############################################################################
set -euo pipefail
shopt -s nullglob
# ---------------- command‑line args ------------------------------------------
dir="." # directory to scan
glob="*" # file glob to match
mapfile="" # mapping table
do_move=false # dry‑run by default
print_help() {
cat <<EOF
Usage: $(basename "$0") -m MAPFILE [options]
Options:
-m FILE Mapping file (two columns: key value)
-d DIR Directory to work in (default: .)
-g GLOB Shell glob to restrict files (default: "*")
--apply Actually perform the renames
-h Show this help
The script replaces every occurrence of KEY in a filename by its VALUE
as defined in MAPFILE. Keys that are not found in the map are reported.
EOF
exit 0
}
# parse args
while [[ $# -gt 0 ]]; do
case $1 in
-m) mapfile=$2; shift 2 ;;
-d) dir=$2; shift 2 ;;
-g) glob=$2; shift 2 ;;
--apply) do_move=true; shift ;;
-h|--help) print_help ;;
*) echo "❌ Unknown option: $1" >&2; print_help ;;
esac
done
[[ -z $mapfile ]] && { echo "❌ Missing -m MAPFILE"; print_help; }
# ---------------- load mapping file ------------------------------------------
declare -A map
while IFS=$'\t' read -r key val _; do
# remove CRs & trailing blanks
key=${key//$'\r'/}; key=${key%%[[:space:]]*}
val=${val//$'\r'/}; val=${val%%[[:space:]]*}
[[ $key && $val ]] && map["$key"]="$val"
done < "$mapfile"
[[ ${#map[@]} -eq 0 ]] && { echo "❌ No key/value pairs found in $mapfile"; exit 1; }
# ---------------- rename loop ------------------------------------------------
cd "$dir"
for old in $glob; do
[[ -e $old ]] || continue
new=$old
replaced=false
# replace FIRST matching key (break after substitution)
for key in "${!map[@]}"; do
if [[ $new == *"$key"* ]]; then
new=${new//$key/${map[$key]}}
replaced=true
break
fi
done
if ! $replaced; then
printf "⚠️ No mapping found for %s (skipped)\n" "$old" >&2
continue
fi
if $do_move; then
mv -v "$old" "$new"
else
printf "mv -v '%s' '%s'\n" "$old" "$new"
fi
done
$do_move || echo $'\nDry‑run only. Re‑run with --apply to rename.'

rename_by_map.sh – A Generic Map‑Driven File Renamer

A small, portable Bash script that batch‑renames files by substituting a token in each filename with a replacement value taken from a two‑column mapping table.

Why another renamer?
• Works with any file type.
• Dry‑run by default (zero‑risk).
• Handles Windows line endings automatically.
• Only a Bash ≥ 4 shell required—no external dependencies.


1  Script quick‑start

# Preview renames (recommended)
./rename_by_map.sh -m mapping.tsv -g "*.cov"

# If preview looks correct, apply changes
./rename_by_map.sh -m mapping.tsv -g "*.cov" --apply
Flag Purpose Default
-m FILE Mapping file (two whitespace‑separated columns) required
-d DIR Directory to process .
-g GLOB Shell glob restricting which files are touched *
--apply Perform renames (omit for dry‑run) false

2  Mapping file format

oldToken    newToken
ind01       GTEX-1IL2V
ind02       GTEX-15DDE
IMG0001     Vacation_0001

Tabs or spaces both work.
Windows CRLF endings are stripped automatically.


3  Typical scenarios

Goal Command
Rename Black_ind??_chr*.cov to GTEx IDs ./rename_by_map.sh -m ind_map.txt -g "Black_*.cov" --apply
Rename all FASTQ files in /data/run42 ./rename_by_map.sh -d /data/run42 -m sample_map.tsv -g "*.fq.gz" --apply
Preview renaming JPEGs in images/ ./rename_by_map.sh -d images -m pic_map.txt -g "*.jpg"

4  Sample dry‑run

mv -v -- 'Black_ind03_chr14.cov' 'Black_GTEX-13SLX_chr14.cov'
mv -v -- 'Black_ind03_chr15.cov' 'Black_GTEX-13SLX_chr15.cov'
…

5  Live output (--apply)

‘Black_ind03_chr14.cov’ -> ‘Black_GTEX-13SLX_chr14.cov’
‘Black_ind03_chr15.cov’ -> ‘Black_GTEX-13SLX_chr15.cov’
…

6  Troubleshooting

Symptom Likely cause / fix
⚠️ No mapping found for … Filename lacks a token present in the map. Add the mapping or ignore.
Garbled preview lines Hidden \r in mapping file; clean with dos2unix or rely on built‑in stripping.
“No key/value pairs found” Mapping file mis‑formatted or empty.

7  How the script works

  1. Load mapping: Reads MAPFILE into an associative array (map[key]=value), stripping \r and trailing whitespace.
  2. Scan files: cd DIR and iterate over GLOB.
  3. Substitute: For each filename, replace the first matching key with its value.
  4. Dry‑run v. live: Without --apply, prints mv commands; with it, executes them.
  5. Safety nets: set -euo pipefail + warnings for unmapped keys.

8  Requirements

  • Bash ≥ 4 (associative arrays).
  • Standard POSIX tools (mv, printf).
  • Linux, macOS, or any UNIX‑like environment (WSL, Git‑Bash on Windows).

9  License

MIT – © 2025 C J & Contributors. Feel free to adapt and redistribute.

ind01 GTEX-1IL2V
ind02 GTEX-15DDE
ind03 GTEX-13SLX
ind04 GTEX-131XH
ind05 GTEX-1KANB
ind06 GTEX-1JMQL
ind07 GTEX-1117F
ind08 GTEX-ZAJG
ind09 GTEX-1JMLX
ind10 GTEX-1JMPZ
@KuRRe8
Copy link

KuRRe8 commented Jun 2, 2025

nice utility

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment