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