There are some extensionless files in current directory (there are not dot in their names).

I want to rename like so:

filename -> filename.md

I tried doing mv * *.md but that didn't help.

I am interested in a function that takes an argument for the extension and renames accordingly, and the another one for the files to perform operation on.


Patterns are matched by the shell, not by the command, so what you tried had no chance to work: first the shell expands * to all files (not just the extensionless ones: you never said anything about the files not having a . in their name), and *.md to all files whose name in .md; then the shell passes the concatenation of the two lists to the mv command.

In zsh

In zsh, run the following command once (put them in your ~/.zshrc for the future):

autoload -U zmv
# you don't need the following two now, but put them also in your .zshrc
alias zcp='zmv -C'
alias zln='zmv -L'

You can then run zmv to rename files according to a pattern. Both the pattern and the replacement text need to be quoted so that they are passed-as is to the zmv function which will expand them in due course.

zmv '^*.*' '$f.md'

Note the single quotes around both arguments: the pattern and the replacement expression must be passed literally to the zmv function.

^*.* means all files except the ones matching *.*, it's a shortcut for *~*.* (both are zsh extensions to the traditional pattern syntax). If you want to use this pattern outside zmv, you need to run setopt extended_glob first (put that in your .zshrc too).

In bash

Bash has no such convenient tool, so you have to resort to a loop. First, activate ksh globbing extensions; that's something you should put in yout ~/.bashrc.

shopt -s extglob

Now you can use the !(PATTERN) operator to match extensionless files.

for x in !(*.*); do
  mv -- "$x" "$x.md"

The double quotes arond $x are necessary in case the file names contain whitespace or globbing characters. The -- after the command name is necessary in case a file name begins with -.

In any shell

If you don't have the !(…) operator, you can loop over all files and test each file name inside the loop.

for x in *; do
  case "$x" in
    *.*) ;;         # skip this file
    *) mv -- "$x" "$x.md";;
  • @StephaneChazelas Ah, yes, POSIX does allow an empty command list in case items. I think I got into the habit of using : from an old shell that didn't. I don't remember which one that would be. Mar 23 '13 at 22:33

You can do this with rename (the one that comes with perl sometimes renamed prename). If there are other files in the directory, you'll need to exclude those as follows with an extglob:

shopt -s extglob
rename s/$/.md/ -- !(*.*)
  • That don't do anything. rename --version says rename from util-linux 2.22.1. Mar 23 '13 at 17:11
  • Oh, this is rename as shipped with perl. You might need to use prename instead. Mar 23 '13 at 17:14
  • There is no prename here on Fedora 18. I have perl v5.16.2 Mar 23 '13 at 17:17
  • Hmm, minus one for fedora then. Grab it from kaarsemaker.net/static/downloads/prename - it's a pretty short and simple perl script. Mar 23 '13 at 17:24
  • 1
    !(patterns) is extended pattern matching operator in BASH (switched on by shopt -s extglob) that matches anything except one of the given patterns. Look for extglob in man bash.
    – peterph
    Mar 23 '13 at 18:07

Found a short answer here (that also supports renaming, which is what I was looking for when I found this page):

for f in *.OLDNAME; do mv $f `basename $f .OLDNAME`.NEWNAME; done;

This also works to add or remove extensions, of course:

# Adding extension
for f in *; do mv $f ${f}.NEWNAME; done; # No need to use basename here -- just adding a new extension

# Removing extension
for f in *.OLDNAME; do mv $f `basename $f .OLDNAME`; done;
  • Doesn't really work if the file name contains spaces.
    – adib
    May 3 '15 at 4:31

There may be a simpler way, but this one is fairly straightforward and the general form is adaptable to all kinds of purposes:

for name in *; do 
    if [ -f "$name" ]; then 
        mv -- "$name" "$name.md"

It could also be turned into a function.

  • Can you make it more readable, I mean can you spread it on multiple lines with proper indentation? Mar 23 '13 at 17:28
  • These f s are confusing me. Is f (between for and in) and $f same? Mar 23 '13 at 17:32
  • 1
    This renames all the files in the current directory, not just the extensionless ones. Mar 23 '13 at 20:56
  • @Gilles: True. Rather than expand upon this, since you brought the definitive bomb anyway, I'll just +1 your answer. And all apologies to Santosh for my original use of the one letter variable name, that was thoughtless.
    – goldilocks
    Mar 23 '13 at 22:32

Not the answer you're looking for? Browse other questions tagged or ask your own question.