views:

130

answers:

3

I'd like to change the file name suffix from files (using a bash script), but sometimes there are files with one period and some with two.

Now I use this:

new_file=`echo ${file} | sed 's/\(.*\.log.*\)'${suf}'/\1.'${num}'/'`

Where 'new_file' is the new file name, 'file' the original file name, '${suf}' the file's suffix and ${num} a new number.

So some.log must become some.log.1 and some.log.1 must become some.log.2. With my code some.log becomes some.log.1, but some.log.1 remains some.log.1.

I hope I'm clear enough. I appreciate any advice (even not using sed).

+1  A: 

If you're looking to roll over log files, and depending on how complex you need to get, I've used the following segment before:

#!/usr/bin/bash
# rollover.sh
#   Rolls over log files in the current directory.
#     *.log.8 -> *.log.9
#     *.log.7 -> *.log.8
#     : : :
#     *.log.1 -> *.log.2
#     *.log   -> *.log.1

shft() {
    # Change this '8' to one less than your desired maximum rollover file.
    # Must be in reverse order for renames to work (n..1, not 1..n).
    for suff in {8..1} ; do
        if [[ -f "$1.${suff}" ]] ; then
            ((nxt = suff + 1))
            echo Moving "$1.${suff}" to "$1.${nxt}"
            mv -f "$1.${suff}" "$1.${nxt}"
        fi
    done
    echo Moving "$1" to "$1.1"
    mv -f "$1" "$1.1"
}

for fspec in *.log ; do
    shft "${fspec}"
    #date >"${fspec}" #DEBUG code
done

This will automatically roll over log files up to version 9 although you can just change the suff for loop to allow more.

With that DEBUG added so new files are created automatically for testing, the following transcript shows it in action:

pax> touch qq.log ; ./rollover.sh
Moving "qq.log" to "qq.log.1"

pax> touch "has spaces.log" ; ./rollover.sh
Moving "has spaces.log" to "has spaces.log.1"
Moving "qq.log.1" to "qq.log.2"
Moving "qq.log" to "qq.log.1"

pax> ll *log*
-rw-r--r-- 1 pax None 30 2010-09-11 20:39 has spaces.log
-rw-r--r-- 1 pax None  0 2010-09-11 20:39 has spaces.log.1
-rw-r--r-- 1 pax None 30 2010-09-11 20:39 qq.log
-rw-r--r-- 1 pax None 30 2010-09-11 20:38 qq.log.1
-rw-r--r-- 1 pax None  0 2010-09-11 20:38 qq.log.2

The good thing about this script is that it's easily configurable to handle a large amount of history (by changing the {8..1} bit), handles names with spaces, and handles gaps relatively robustly if log files go missing.

paxdiablo
Tried to create my own log rotating script, but this one looks better. It looks almost as needed :-)))) After *some.log.9* (number 10), not the oldest log file is removed, but the second oldest is removed. The oldest always remains. How can i fix that ?
Robertico
@Robertico, if you're storing 1 through 9, then 8 will overwrite 9 so the oldest will automatically be "deleted". If you already have a 10, just change for `suff` loop to the oldest one you want minus 1. So, if you want to keep 1 thru 50, the loop should be `{49..1}`.
paxdiablo
@paxdiablo, changing 'for suff in {8..1} ;' into 'for suff in {1..9} ;' doesn work. It renames *some.log* to *some.log.1* after that *some.log.1* becomes *some.log.10* and *some.log* becomes *some.log.1*. Running the script again. *some.log.10* remains, *some.log.1* is deleted and *some.log* becomes *some.log.1*. A little bit confusing :-))
Robertico
@Robertico, you need the loop in the reverse order so that the files are renamed in that order. In other words, `{1..9}` in _not_ correct, it should be `{9..1}`.
paxdiablo
@paxdiablo. I guess I do not understand. As mentioned before when is use `{9..1}` the the second oldest log file is removed and not the oldest. So i tried `{1..9}` as you mentioned in your second comment >>*if you're storing 1 through 9, then 8 will overwrite 9 so the oldest will automatically be "deleted".*<<
Robertico
@Robertico, what log files exist on your system? What is the output of the script when you run it? I tested this script and it ran fine, removing the oldest. If you're using 9..1, you should see the messages "moving 9 to 10", "8 to 9", ... "1 to 2", "log to 1". That's assuming you're only keeping up to .10 as the oldest.
paxdiablo
A: 

To rotate logs, you should really use logrotate.

If you can't rely on logrotate being available, here's a way do it inside the shell. To keep things simple, I'll assume that nothing else (including another instance of your script) will try to rename the log files while your script is running.

The easiest approach is to recursively rename log N+1 before actually renaming log N to N+1. The shell can perform all the necessary arithmetic, you don't need sed here. Note that while recursive functions are possible in a POSIX shell, there are no local variables other than the positional parameters (many shells offer local variables as an extension).

#!/bin/sh
## Move "$1.$2" to "$1.$(($2+1))", first rotating the target as well.
rotate () {
  if [ -e "$1.$(($2+1))" ]; then rotate "$1" $(($2+1)); fi
  mv -- "$1.$2" "$1.$(($2+1))"
}

for x; do
  ## Break each argument into FILE.NUMBER or just FILE.
  suffix=${x##*.}
  case $suffix in
    *[!0-9]*)
      if [ -e "$x.0" ]; then rotate "$x" 0; fi
      mv -- "$x" "$x.0";;
    *) rotate "${x%.*}" "$suffix";;
  esac
done

Regarding what you've written, note that echo ${file} is bad for two reasons: most importantly, if ${file} contains any special characters such as white space, the shell will interpret them; also, with some shells, echo itself will interpret backslashes and possibly a leading -. So you should always write printf %s "$file" instead.

Gilles
Thx. For the example.
Robertico
A: 

@paxdiablo. Something went wrong testing i think.

Now i use this piece of code as test;

#!/usr/bin/bash

        shft() {
            for suff in {6..1} ; do
                if [[ -f "$1.${suff}" ]] ; then
                    ((nxt = suff + 1))
                    echo Moving "$1.${suff}" to "$1.${nxt}"
                    mv -f "$1.${suff}" "$1.${nxt}"
                fi
            done
            echo Moving "$1" to "$1.1"
            mv -f "$1" "$1.1"
        }

        clear

        folder=~/logs/*.log

        for i in {1..20}; do
            echo ${i}> ~/logs/some.log 

            for fspec in ${folder} ; do
                    shft "${fspec}"
            done
        done

Every thing works fine now. Sorry for the confusion.

Robertico