Rename Many Files with a for Loop in Bash and POSIX sh
Rename every .txt file to .md with a for loop and suffix removal around mv --. An unmatched glob stays literal in Bash unless nullglob is set.
A directory full of .txt files needs a new extension, and the command has to cope with spaces in the names.
touch a.txt 'b c.txt' c.log
for f in *.txt; do mv -- "$f" "${f%.txt}.md"; done
ls
echo "--- same loop with nothing left to match"
for f in *.txt; do mv -- "$f" "${f%.txt}.md"; done
shopt -s nullglob
for f in *.txt; do mv -- "$f" "${f%.txt}.md"; done
echo "nullglob: loop skipped, exit $?"a.md
b c.md
c.log
--- same loop with nothing left to match
mv: cannot stat '*.txt': No such file or directory
nullglob: loop skipped, exit 0The loop assigns each match to f, ${f%.txt} strips the old suffix, and quoting both arguments keeps b c.txt in one piece, giving b c.md. The -- stops mv from reading a name that starts with a dash as an option. When no file matches, Bash passes the pattern itself to the loop, and the second run shows mv complaining about a file called *.txt. shopt -s nullglob makes an empty match expand to nothing instead. See mv invocation.
mv overwrites an existing .md file without asking, so add -n or -i when a collision is possible. The loop starts one mv per file, which is fine for thousands of files.