2

under the following current folder, we have the example of files

lok.log.1
df.log.6
weq.log.90
vr.log.11
vs.aw.frsd.log.3

we want to increase the files extension by 1

so the expected output will be as the following

lok.log.2
df.log.7
weq.log.91
vr.log.12
vs.aw.frsd.log.4

please advice how to rename the files with find and regex

  • The concept should be all the files that have ".log" will be renamed by +1
  • I'm not sure what you mean by "with find and regex" - if you have one of the perl-based rename commands, you could do something like rename -- 's/(\d+)$/$1+1/e' *.log.* I think? – steeldriver Oct 3 '18 at 15:54
5

With zsh:

autoload zmv # best in ~/.zshrc
zmv -n -f '(*.log.)(<->)(#qnOn)' '$1$(($2+1))'

(remove the -n if satisfied with the result)

  • <-> matches any decimal number
  • (#qnOn): glob qualifier here to sort the list of files numerically (n) in reverse order by name (On) so file.log.2 is renamed to file.log.3 before file.log.1 is renamed to file.log.2. Add . if you want to rename only regular files (but then you would probably want to add a -o-nT option assuming GNU mv) and D if you also want to rename hidden files.
  • -f disables the safeguard that would cancel that command when a file would be renamed to an existing file which would get in the way in our file.log.1, file.log.2 example above. It would still guard against both foo.log.1 and foo.log.01 being renamed to foo.log.2.

Recursively:

zmv -n -f '(**/)(*.log.)(<->)(#qnOn)' '$1$2$(($3+1))'

From bash or sh or ksh:

zsh << 'EOF'
autoload zmv
zmv -n -f '(*.log.)(<->)(#qnOn)' '$1$(($2+1))'
EOF

With bash and without using zsh, and if you have GNU ls and GNU mv, and the list of files is not too big, you can do something approaching with:

shopt -s failglob
shopt -s extglob
export LC_ALL=C
eval "files=($(
  ls --quoting-style=shell-always -rvd -- *.log.+([[:digit:]])))"
for f in "${files[@]}"; do
   echo mv -nT -- "$f" "${f%.*}.$((10#${f##*.} + 1))"
done

(remove echo when satisfied).

Recursively, with GNU bash, GNU find, GNU mv and GNU sort, for regular files only:

export LC_ALL=C
while IFS= read -rd '' -u3 file; do
  echo mv -nT "$file" "${file%.*}.$((10#${file##*.} + 1))"
done 3< <(
  find . -name '.?*' -prune -o -regex '.*\.log\.[0-9]+' -type f -print0 |
    sort -rzV)

mv -n is a GNU extension to avoid clobbering existing files, and -T to remove the ambiguity between move to and move into which mv otherwise suffers from. Note however that when a file is not renamed because of -n, you don't get any error about that.

  • should be under shell script as bash / ksh / sh – yael Oct 3 '18 at 15:39
  • @yael, see edit. – Stéphane Chazelas Oct 3 '18 at 15:40
  • autoload zmv bash: autoload: command not found... – yael Oct 3 '18 at 15:41
  • any way as I asked we prefer to do the rename with find and regex – yael Oct 3 '18 at 15:43
  • 1
    Note that in all solutions this will happen: mv -- file.log.00024 file.log.25. Don't think that that is what is expected. Maybe something like zmv -n -f '(*.log.)(<->)(#qnOn)' '$1$(printf %0\*d ${#2} $(($2+1)))' and similar for bash. – Isaac Oct 4 '18 at 6:10
0

rename is perl-based and allows using arithmetics (some distros use the name prename for this perl renaming tool) :

rename 's/(log.)([0-9]+)$/$1.($2+1)/e' <files>

To combine with find, use:

find <search options> -exec rename 's/(log.)([0-9]+)$/$1.($2+1)/e' {} +

Edit following Stéphane's comment:

The unsorted output of find might indeed trouble you and it better be sorted beforehand. We can use reverse (!) sort and its version numbering option for the given name patterns:

find <search parpameters> -print0 |\
   sort -rzV |\
   xargs -r0 rename 's/(log.)([0-9]+)$/$1.($2+1)/e'
  • 3
    Beware find processes files in an unsorted way. If it finds file.log.1 before file.log.2, that won't work properly. – Stéphane Chazelas Oct 4 '18 at 7:27
  • @StéphaneChazelas good point. sort will help out here. – Fiximan Oct 4 '18 at 10:10
  • 1
    You want reverse version sort here. You don't need to call one rename per file. xargs -r0 rename ... should be enough and more efficient. See my answer for other caveats. – Stéphane Chazelas Oct 4 '18 at 10:49
  • rename seems to be incredibly slow in the first place. – Fiximan Oct 4 '18 at 11:01
  • 1
    slow to start (as it's perl) but not slow to run. Beware there are different variants of perl's rename, some which will take care of not overwriting files by default (but fail in some corner cases), some that won't. In any case, calling rename on many files should be significantly faster that calling one mv for each of those files (unless you use a shell with mv builtin like zsh/ksh93 where mv can be made builtin). – Stéphane Chazelas Oct 4 '18 at 11:05

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.