7

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.

4 Answers4

14

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"
done

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";;
  esac
done
  • @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. – Gilles 'SO- stop being evil' Mar 23 '13 at 22:33
3

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;
dantiston
  • 131
3

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/ -- !(*.*)
2

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"
    fi
done

It could also be turned into a function.

goldilocks
  • 87,661
  • 30
  • 204
  • 262