Any command line tools to rename files that don't already contain a string or a set of strings to then include that string?

So all files in a given folder should have high in the name for "high priority", but some do and some don't. Rather than add it to all files, how may I first check to see if a file has high in the name then, if it doesn't, add it to the beginning of the name?


If you have GNU Parallel:

parallel mv {} '{= /high/ or $_="high$_" =}' ::: *
  • What is the benefit of parallel here? – xenoid May 25 '20 at 9:20
  • The syntax. So if you already have GNU Parallel installed and you do not have perl rename installed, you can use that instead. In other words: the parallelization part is not the goal. – Ole Tange May 25 '20 at 16:47

With zsh:

autoload zmv # best in ~/.zshrc
zmv '^*high*' 'high-$f'

With case-insensitive matching:

zmv '^*(#i)high*' 'high-$f'

If you're using bash, then the pattern !(*high*) will match all names in the current directory that doesn't contain the string high, if the extglob shell option is set:

$ shopt -s extglob
$ ls
1      2      3      4-high
$ ls -d -- !(*high*)
1 2 3

We may then loop over the names matching this pattern, renaming each matching name:

shopt -s extglob nullglob

for name in !(*high*); do
    [[ ! -f $name ]] && continue

    mv -- "$name" "high-$name"

What's happening here is that we set extglob and also nullglob (makes the globbing pattern disappear rather than be retained as-is if the pattern does not match) before the loop. You could also enable dotglob if you want to rename hidden names as well.

The loop then skips any name that does not refer to a regular file (it may be a directory name, for example), and then inserts the string high- at the front of the name with mv.

The -- in the mv command is to avoid interpreting any filenames that start with a dash as a set of options by mistake.

Running this on a set of test files:

$ ls
1      2      3      4-high

Just pasting in the code from above straight into the shell:

$ shopt -s extglob nullglob
$ for name in !(*high*); do
>     [[ ! -f $name ]] && continue
>     mv -- "$name" "high-$name"
> done
$ ls
4-high high-1 high-2 high-3

You could also use the Perl rename utility like so:

shopt -s extglob
rename -v 'if (-f) { s/^/high-/ }' -- !(*high*)

This would move the -f test of the loop in the first variation in this answer into Perl, but still uses the same filename globbing pattern to select the files to rename. The -f makes the renaming only affect regular files (no directories etc.)

A variation that does not rely on extended globbing patterns in the shell would be

rename -v 'if (-f && !/high/) { s/^/high-/ }' -- *

This simply does the test for whether the name already contains the string high inside the rename call, and skips renaming the file if it does occur. The * glob selects all files in the current directory (apart from hidden ones).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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