更改文件名后缀(使用sed?)

4
我希望能够通过 Bash 脚本更改文件名后缀,但是有时候文件名中只含有一个句点,而有些文件名则包含两个。
目前我使用以下命令:
new_file=`echo ${file} | sed 's/\(.*\.log.*\)'${suf}'/\1.'${num}'/'`

其中,'new_file' 是新文件名,'file' 是原始文件名,'${suf}' 是文件的后缀,${num} 是一个新数字。

因此,some.log 必须变成 some.log.1some.log.1 必须变成 some.log.2。使用我的代码,some.log 变成了 some.log.1,但是 some.log.1 仍然是 some.log.1

希望我表达得清楚。感谢任何建议(即使不使用 sed)。

更新:

@paxdiablo。我想测试时出了问题。

现在我使用这段代码进行测试;

#!/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

现在一切都正常了。对于造成的困惑,我表示抱歉。


1
建议采用更好的命名方案,以避免头疼。例如,some.log 可以变成 some.log.20100911-013024;下一个 some.log 变成 some.log.20100911-084137 等等,其中后缀是日志切换时的日期/时间。这样可以避免一次重命名多个文件。名称会自动按时间顺序排序。唯一的缺点是您没有备份日志数量的限制。使用 find . -mtime +183 -name 'some.log.*' -exec rm {} \; 处理超过 6 个月的日志;根据您的周期进行调整。 - Jonathan Leffler
你能否使用问题来提供更新,而不是使用答案来达到这个目的。另外,如果你必须说“谢谢”,能否在评论中表达,而不是在答案中:http://blog.stackoverflow.com/2011/01/how-to-say-thanks-in-an-answer/ 。非常感谢。 - Kev
2个回答

5

如果您想滚动日志文件,根据您需要的复杂程度,我之前使用过以下代码段:

#!/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

这将自动滚动日志文件,最多可滚动到第9个版本,尽管您可以更改suff for循环以允许更多版本。
添加了DEBUG以便于测试自动创建新文件,以下记录显示其工作原理:
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

这个脚本的好处在于它可以轻松配置以处理大量历史记录(通过更改{8..1}位),处理带有空格的名称,并且如果日志文件丢失,可以相对稳健地处理间隙。

尝试创建自己的日志轮转脚本,但这个看起来更好。它几乎符合需求 :-)))) 在 some.log.9(第10个)之后,不是删除最旧的日志文件,而是删除第二旧的日志文件。最旧的始终保留。我该如何修复这个问题? - John Doe
@Robertico,如果您正在存储1到9,则8将覆盖9,因此最旧的将自动“删除”。如果您已经有10个,请将suff循环更改为您想要的最旧的一个减1。因此,如果您想保留1到50,则循环应为{49..1} - paxdiablo
@paxdiablo,将“for suff in {8..1} ;”更改为“for suff in {1..9} ;”不起作用。它会将some.log重命名为some.log.1,然后some.log.1变成了some.log.10,而some.log变成了some.log.1。再次运行脚本。some.log.10保持不变,some.log.1被删除,而some.log变成了some.log.1。有点令人困惑 :-)) - John Doe
@Robertico,你需要将循环顺序反过来,这样文件就会按照相应的顺序进行重命名。换句话说,{1..9}是不正确的,应该改为{9..1} - paxdiablo
@paxdiablo。我想我不明白。如之前所述,当使用{9..1}时,会删除第二旧的日志文件而不是最旧的。因此,我尝试了你在第二条评论中提到的{1..9}>>如果您存储1到9,则8将覆盖9,因此最旧的将自动被“删除”。<< - John Doe
@Robertico,你的系统上有哪些日志文件?当你运行脚本时输出是什么?我测试了这个脚本并且它顺利运行,删除了最老的那个。如果你使用版本9..1,你应该会看到类似于"moving 9 to 10", "8 to 9", ... "1 to 2", "log to 1" 的消息。这是假设你只保留最老的.10版本。 - paxdiablo

2

为了轮换日志,你应该真正使用logrotate

如果你不能依赖于可用的logrotate,这里有一种在shell内部执行的方法。为了保持简单,我假设在你的脚本运行时没有其他东西(包括另一个实例)尝试重命名日志文件。

最简单的方法是在实际将日志N重命名为N+1之前,递归地重命名日志N+1。Shell可以执行所有必要的算术运算,这里不需要sed。请注意,虽然在POSIX shell中可以使用递归函数,但除了位置参数以外没有局部变量(许多shell作为扩展提供局部变量)。

#!/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

关于你所写的内容,需要注意的是echo ${file}有两个问题:最重要的是,如果${file}包含任何特殊字符,比如空格,shell会解释它们;此外,对于某些shell,echo本身会解释反斜杠和可能的前导-。因此,你应该始终使用printf %s "$file"来代替。


正是我所需要的。谢谢。 - joshsh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接