只有在不存在的情况下,才会在ini部分的末尾插入一行

时间:2015-04-22 13:21:09

标签: awk sed sh ini samba

我有一个smb.conf ini文件,只要使用某个GUI工具进行编辑就会覆盖该文件,从而消除了自定义设置。这意味着我需要一个cron作业来确保文件中的一个特定部分包含某个option=value对,如果它不存在则将其插入该部分的末尾。

示例

确保hosts deny=192.168.23.部分中存在[myshare]

  [global]
    printcap name = cups
    winbind enum groups = yes
    security = user
[myshare]
    path=/mnt/myshare
    browseable=yes
    enable recycle bin=no
    writeable=yes
    hosts deny=192.168.23.
 [Another Share]
    invalid users=nobody,nobody
    valid users=nobody,nobody
    path=/mnt/share2
    browseable=no

使用awk的长篇解决方案

经过很长一段时间与sed挣扎,我得出结论,这可能不适合这项工作。所以我搬到了awk并想出了这个:

#!/bin/sh
file="smb.conf"
tmp="smb.conf.tmp"
section="myshare"
opt="hosts deny=192.168.23."

awk '
    BEGIN {
        this_section=0;
        opt_found=0;
    }
    # Match the line where our section begins
    /^[ \t]*\['"$section"'\][ \t]*$/ {
        this_section=1;
        print $0;
        next;
    }
    # Match lines containing our option
    this_section == 1 && /^[ \t]*'"$opt"'[ \t]*$/ {
        opt_found=1;
    }
    # Match the following section heading
    this_section == 1 && /^[ \t]*\[.*$/ {
        this_section=0;
        if (opt_found != 1) {
            print "\t'"$opt"'";
        }
    }
    # Print every line
    { print $0; }
    END {
        # In case our section is the very last in the file
        if (this_section == 1 && opt_found != 1) {
            print "\t'"$opt"'";
        }
    }
' $file > $tmp

# Overwrite $file only if $tmp is different
diff -q $file $tmp > /dev/null 2>&1
if [ $? -ne 0 ]; then
    mv $tmp $file
    # reload smb.conf here
else
    rm $tmp
fi

我不禁觉得这是一个很长的脚本来完成一个简单的任务。是否有更高效/更优雅的方法使用sed和awk等基本shell工具在ini文件中插入属性?

4 个答案:

答案 0 :(得分:1)

考虑使用Python 3的configparser

#!/usr/bin/python3

import sys
from configparser import SafeConfigParser

cfg = SafeConfigParser()
cfg.read(sys.argv[1])

cfg['myshare']['hosts deny'] = '192.168.23.';

with open(sys.argv[1], 'w') as f:
    cfg.write(f)

被称为./filename.py smb.conf(即,第一个参数是要更改的文件)。

请注意,此处不会保留注释。但是,由于GUI会覆盖配置并且不保留自定义选项,因此我怀疑注释已经被破坏,并且这不是您的担忧。

答案 1 :(得分:0)

未经测试,应该可以正常工作

awk -vT="hosts deny=192.168.23" 'x&&$0~T{x=0}x&&/^ *\[[^]]+\]/{print "\t\t"T;x=0}
                                /^ *\[myshare\]/{x++}1' file

答案 2 :(得分:0)

这个解决方案有点尴尬。它使用INI节头作为记录分隔符。这意味着在第一个标题之前有一个空记录,所以当我们匹配我们感兴趣的标题时,我们必须阅读 next 记录来处理INI部分。此外,还有一些printf命令,因为记录仍包含前导和尾随换行符。

awk -v RS='[[][^]]+[]]' -v str="hosts deny=192.168.23." '
    {printf "%s", $0; printf "%s", RT} 
    RT == "[myshare]" {
        getline
        printf "%s", $0
        if (index($0, str) == 0) print str
        printf "%s", RT
    }
' smb.conf

RS是包含将文本拆分为记录的正则表达式的awk变量 RT是包含当前记录分隔符的实际文本的awk变量。

答案 3 :(得分:0)

使用GNU awk进行几个扩展:

$ cat tst.awk
index($0,str) { found = 1 }
match($0,/^\s*\[([^]]+).*/,a) {
    if ( (name == tgt) && !found ) { print indent str }
    name = a[1]
    found = 0
}
{ print; indent=gensub(/\S.*/,"","") }

$ awk -v tgt="myshare" -v str="hosts deny=192.168.23." -f tst.awk file
[global]
    printcap name = cups
    winbind enum groups = yes
    security = user
[myshare]
    path=/mnt/myshare
    browseable=yes
    enable recycle bin=no
    writeable=yes
    hosts deny=192.168.23.
[Another Share]
    invalid users=nobody,nobody
    valid users=nobody,nobody
    path=/mnt/share2
    browseable=no

$ awk -v tgt="myshare" -v str="fluffy bunny" -f tst.awk file
[global]
    printcap name = cups
    winbind enum groups = yes
    security = user
[myshare]
    path=/mnt/myshare
    browseable=yes
    enable recycle bin=no
    writeable=yes
    hosts deny=192.168.23.
    fluffy bunny
[Another Share]
    invalid users=nobody,nobody
    valid users=nobody,nobody
    path=/mnt/share2
    browseable=no