Method 1: A for Loop With mv
The most portable approach uses the shell you already have. A for loop iterates over the matching files and mv renames each one. To add a prefix to every .txt file: for f in *.txt; do mv -- "$f" "2025_$f"; done. To change an extension, strip the old one with parameter expansion: for f in *.jpeg; do mv -- "$f" "${f%.jpeg}.jpg"; done, where ${f%.jpeg} removes the trailing .jpeg.
Always quote "$f" and use the -- separator so filenames with spaces or leading dashes don't break the command. The loop is limited to shell globbing rather than regex, but for prefixes, suffixes, and extension swaps it's the simplest thing that works, and it needs nothing installed.
You can also add a sequential counter inside the loop to number files as you rename them. A common pattern is i=1; for f in *.jpg; do mv -- "$f" "photo_$(printf '%03d' "$i").jpg"; i=$((i+1)); done, which produces photo_001.jpg, photo_002.jpg, and so on. The printf '%03d' part zero-pads the number so the names sort correctly in a file manager. Because the glob expands in alphabetical order, the files are numbered in that same order, so sort the batch the way you want before you run the loop.
Method 2: find -exec for Files in Subdirectories
When the files aren't all in one folder, find locates them recursively and runs a rename on each. To lowercase-prefix every .log file in a tree: find . -type f -name '*.log' -exec sh -c 'mv "$1" "$(dirname "$1")/archived_$(basename "$1")"' _ {} \;. That looks dense, but the shape is standard: find selects, -exec runs a small shell command per file, and {} is the current path.
For heavy batches, piping find into xargs is faster than spawning a process per file. The key point is that find is how you reach files across a whole directory tree, which a plain glob in one folder can't do.
When filenames may contain spaces or newlines, pair find -print0 with xargs -0 so the two agree on a null byte as the separator instead of whitespace: find . -type f -name '*.log' -print0 | xargs -0 rename 's/^/archived_/'. This safely feeds the whole list to a single rename call. If you only need to touch files in the current directory and one level down, add -maxdepth to find so it doesn't descend into every nested folder.
Method 3: The rename Utility (Regex Power)
The rename command is purpose-built for bulk renaming, and the Perl-based version (default on Debian and Ubuntu, installable elsewhere) accepts a full regular expression. This is where Linux out-muscles simple loops:
- Preview first with -n (dry run): rename -n 's/ /_/g' *.txt prints what would change without doing it.
- Replace spaces with underscores: rename 's/ /_/g' *.txt
- Strip a prefix: rename 's/^IMG_//' *.jpg
- Change an extension: rename 's/\.jpeg$/.jpg/' *.jpeg
- Reorder a captured date: rename 's/(\d{4})-(\d{2})-(\d{2})/$3-$2-$1/' *.pdf
Note that some distributions ship the simpler util-linux rename, which does literal from-to substitution rather than regex (rename .jpeg .jpg *.jpeg). Check which you have with rename --version, and install the Perl version (often the rename or perl-rename package) if you need regular expressions.
Method 4: mmv and zmv for Pattern Renames
mmv renames by matching a wildcard pattern and mapping it to another: mmv '*.txt' '#1.bak' renames every .txt to .bak, where #1 refers to the first wildcard. It's concise for pattern-to-pattern jobs once installed. And for zsh users, zmv (load it with autoload -U zmv) offers similarly powerful renaming with regex-like matching built into the shell, zmv '(*).txt' '${1}_backup.txt' is a common form. Both are optional power tools; the mv loop and the rename utility cover the vast majority of real needs.