2

Shell: zsh

Hello folks, I am trying to quickly change the file extension of a file like in DOS.

I did find this question about it (sort of) it still does not answer the question: Rename multiple files with mv to change the extension

I want to change the extension of a file on a specific directory faraway from my pwd so when I do mvI don't want to retype again the same directory, i.e:

mv /Users/dir/dir1/moredir/long/foo.txt /Users/dir/dir1/moredir/long/foo.yaml

A n00b like me would simply assume you can go /Users/dir/dir1/moredir/long/foo.txt *.yaml, but this is not the case.

Is there any simple way to achieve this?

Thanks a lot in advance, and wishing you a great weekend!

2 Answers2

1

For a single file, you can use brace expansion to construct a command line to rename a file, where only one part of the file name changes.

mv /Users/dir/dir1/moredir/long/foo.{txt,yaml}

The general syntax is mv UNCHANGED1{OLD,NEW}UNCHANGED2, which expands to mv UNCHANGED1OLDUNCHANGED2 UNCHANGED1NEWUNCHANGED2 before running mv.


For multiple files, the zsh function zmv provides convenient ways to move and rename files. Put this in your .zshrc, or run it from the command line in each session where you want to use zmv:

autoload -zU zmv

Then, to change the extension of files in the current directory, you can run any of these. Note that you need single quotes around the arguments, because the * and $ need to be interpreted inside zmv and not before invoking zmv.

zmv '*.txt' '$f:r.yaml'
zmv '(*).txt' '$1.yaml'
zmv -w '*.txt' '$1.yaml'
zmv -W '*.txt' '*.yaml'

To change the extension of files in another directory, without having to repeat the directory path:

zmv '/Users/dir/dir1/moredir/long/*.txt' '$f:r.yaml'
(cd /Users/dir/dir1/moredir/long && zmv '(*).txt' '$1.yaml')
(cd /Users/dir/dir1/moredir/long && zmv -w '*.txt' '$1.yaml')
(cd /Users/dir/dir1/moredir/long && zmv -W '*.txt' '*.yaml')

In the replacement text:

  • $f is the whole source path.
  • :r is a history modifier which removes the extension from the file name.
  • $1 is the first group in parentheses in the source pattern. Groups in parentheses can't contain directory separators (with one exception that doesn't apply here).
  • The -w flag automatically puts each wildcard in a group.
  • The -W flag causes each * in the replacement text to stand for whatever the corresponding * matches in the source pattern.

(There are several other tools to do that, but they're not part of the default installation on most Unix variants including macOS. Since you're using zsh, take advantage of zmv, which is pretty good at its job.)

0

To rename the single file foo.txt into foo.yaml in the directory /Users/dir/dir1/moredir/long, you may first enter that directory and then run mv:

cd /Users/dir/dir1/moredir/long
mv foo.txt foo.yaml
cd -    # to move back to the original working directory

Or, you may use a brace expansion that expands to the source and target pathnames:

mv /Users/dir/dir1/moredir/long/foo.{txt,yaml}

The above would expand to the full command

mv /Users/dir/dir1/moredir/long/foo.txt /Users/dir/dir1/moredir/long/foo.yaml

... before the shell calls mv. This would obviously only work as long as the file should stay in the same directory after being renamed.

Kusalananda
  • 333,661