You can use zmv
, it's just that some of the escapes are a bit unusual. This will replace all of the spaces and special characters in the names of files in the current directory with _
:
autoload -Uz zmv
q='"'
zmv -nv '*[ \\:\*\?"<>\|]*' '${f//[ \\:\*\?${q}<>\|]/_}'
Some of the pieces:
-n
- no-op. zmv
will report the planned changes but not execute them.
Remove the -n
when you're ready to actually rename the files.
-v
- verbose.
*[...]*
- source pattern. This will match any file that has one of the characters in the [...]
character set.
[...]
- glob pattern to select one of the matching characters.
\\:\*\?"<>\|
- the characters.
\\
, \*
, \?
, \|
- these characters have special meanings in glob patterns and need to be escaped.
, :
, "
, <
, >
- characters that do not need to be escaped.
'${f//[ \\:\*\?${q}<>\|]/_}'
- destination pattern.
${f...}
- f
is set by zmv
to the source filename.
${f//.../_}
- expansion that replaces all matches of a glob pattern with _
.
[ \\:\*\?${q}<>\|]
- the same list of characters as before, with one significant difference. The (e)
parameter expansion flag that zmv
uses apparently cannot handle double quotes in a pattern, but it can accept variables that contain double quotes. Therefore this uses ${q}
, which was set to "
, instead of referencing "
directly.
Note that the patterns do not include /
, i.e. a forward slash. There really isn't a way in unix shell to have a forward slash in a filename, although there is a way to have visible forward slashes in Finder.
Edited to add a more comprehensive zmv
example.
This will rename the regular files in the current directory and subdirectories. The new filenames will have:
- leading whitespace trimmed.
- other consecutive whitespace characters replaced with a single
_
, e.g. aa<space><tab><cr>bb
will become aa_bb
.
- special characters updated based on the mapping in the associative array.
#!/usr/bin/env zsh
local -A map=(
['<']='(' ['>']=')' ['{']='(' ['}']=')'
['*']='.' ['?']='.' ['"']='-' ["'"]='-'
['\']='-' [':']='-' ['|']='-'
)
local ch=${(kj..)map}
newName() {
setopt histsubstpattern extendedglob localoptions
fn=${${1##[[:space:]]##}//[[:space:]]##/_}
print -r -- ${fn:gs/(#b)([$ch])/$map[$match[1]]}
}
autoload -Uz zmv
zmv -nv "(**/)(*[[:space:]$ch]*)(#q.)" '$1$(newName $2)'
zmv
commands for renaming the special characters, so it's hard to say what you did wrong.mv
over each file. Normal parameter substitution should work here, but I would use${var//.../..}
instead of${var/.../...}
, because you want to replace ALL occurances.