【问题标题】:Perl file copy duplicating outputPerl文件复制复制输出
【发布时间】:2012-05-02 23:55:11
【问题描述】:

我正在尝试编写一个菜单驱动的模块化 perl 脚本,该脚本将捕获用户输入并自动执行网络配置过程。该脚本必须能够安装所需的 Arch 软件包,配置 AP 模式,为用户选择的接口配置 DHCP 或静态地址,并提供启用桥接的选项。 (编辑:脚本还需要能够启用和配置 dhcpd 服务)

我现在坚持的部分是创建 rc.conf 文件的备份,读取文件并编辑需要修改的行(如果网络接口已被静态配置)。这个脚本是在 ArchLinux 中使用的,我做了一些搜索,没有找到任何满足我需求的东西。

使用通用输入 $ip = 1.1.1.1; $Bcast = 2.2.2.2; $netmask = 3.3.3.3; $GW = 4.4.4.4;

我花了大约两个小时阅读文件 I/O 并尝试了一些不起作用的方法,包括废弃多文件 IO 方法并使用类似于:while(<IS>){s/^interface.?=(.*)$/"interface=@if[0] \n"/;} 的每个需要的值的输入被替换并且无法让它真正做任何事情。

   if (system ("cat","/etc/rc.conf","|","grep","interface")){   
    use File::Copy "cp";
    $filename = "/etc/rc.conf"; 
    $tempfile = "/etc/rc.tmp";  
    $bak = "/etc/rc.bak";
    cp($filename,$bak);
    open(IS, $filename); 
    open(OS, ">$tempfile"); 
    while(<IS>){ 
        if($_ =~ /^interface.?=(.*)$/){ print OS"interface=@if[0] \n";}
        if($_ =~ /^address.?=(.*)$/){ print OS "address=$ip\n";}
        if($_ =~/^netmask.?=(.*)$/){ print OS "netmask=$netmask\n";}
        if($_ =~/^broadcast.?=(.*)$/){ print OS "broadcast=$Bcast\n";}
        if($_ =~/^gateway.?=(.*)$/){ print OS "gateway=$GW\n"; }
        else {print OS $_;} 
    }
    close(IS); close(OS); 
    unlink($filename); rename($tempfile, $filename);
}

rc.conf 之前

#
# /etc/rc.conf - Main Configuration for Arch Linux

. /etc/archiso/functions

LOCALE_DEFAULT="en_US.UTF-8"
DAEMON_LOCALE_DEFAULT="no"
CLOCK_DEFAULT="UTC"
TIMEZONE_DEFAULT="Canada/Pacific"
KEYMAP_DEFAULT="us"
CONSOLEFONT_DEFAULT=
CONSOLEMAP_DEFAULT=
USECOLOR_DEFAULT="yes"

LOCALE="$(kernel_cmdline locale ${LOCALE_DEFAULT})"
DAEMON_LOCALE="$(kernel_cmdline daemon_locale ${DAEMON_LOCALE_DEFAULT})"
HARDWARECLOCK="$(kernel_cmdline clock ${CLOCK_DEFAULT})"
TIMEZONE="$(kernel_cmdline timezone ${TIMEZONE_DEFAULT})"
KEYMAP="$(kernel_cmdline keymap ${KEYMAP_DEFAULT})"
CONSOLEFONT="$(kernel_cmdline consolefont ${CONSOLEFONT_DEFAULT})"
CONSOLEMAP="$(kernel_cmdline consolemap ${CONSOLEMAP_DEFAULT})"
USECOLOR="$(kernel_cmdline usecolor ${USECOLOR_DEFAULT})"

MODULES=()

UDEV_TIMEOUT=30
USEDMRAID="no"
USEBTRFS="no"
USELVM="no"

HOSTNAME="archiso"

DAEMONS=(hwclock syslog-ng)

interface=eth0
address=192.168.0.99
netmask=255.255.255.0
broadcast=192.168.0.255
gateway=192.168.0.1

rc.conf 之后

#
# /etc/rc.conf - Main Configuration for Arch Linux

. /etc/archiso/functions

LOCALE_DEFAULT="en_US.UTF-8"
DAEMON_LOCALE_DEFAULT="no"
CLOCK_DEFAULT="UTC"
TIMEZONE_DEFAULT="Canada/Pacific"
KEYMAP_DEFAULT="us"
CONSOLEFONT_DEFAULT=
CONSOLEMAP_DEFAULT=
USECOLOR_DEFAULT="yes"

LOCALE="$(kernel_cmdline locale ${LOCALE_DEFAULT})"
DAEMON_LOCALE="$(kernel_cmdline daemon_locale ${DAEMON_LOCALE_DEFAULT})"
HARDWARECLOCK="$(kernel_cmdline clock ${CLOCK_DEFAULT})"
TIMEZONE="$(kernel_cmdline timezone ${TIMEZONE_DEFAULT})"
KEYMAP="$(kernel_cmdline keymap ${KEYMAP_DEFAULT})"
CONSOLEFONT="$(kernel_cmdline consolefont ${CONSOLEFONT_DEFAULT})"
CONSOLEMAP="$(kernel_cmdline consolemap ${CONSOLEMAP_DEFAULT})"
USECOLOR="$(kernel_cmdline usecolor ${USECOLOR_DEFAULT})"

MODULES=()

UDEV_TIMEOUT=30
USEDMRAID="no"
USEBTRFS="no"
USELVM="no"

HOSTNAME="archiso"

DAEMONS=(hwclock syslog-ng)

interface=eth0 
interface=eth0
address=1.1.1.1
address=192.168.0.99
netmask=3.3.3.3
netmask=255.255.255.0
broadcast=2.2.2.2
broadcast=192.168.0.255
gateway=4.4.4.4

【问题讨论】:

  • 你的system() 行引人注目:①返回检查似乎被倒置为Sinan pointed out,②系统的列表调用通常用于绕过shell,但您需要 shell 来设置你的管道,并且不那么戏剧化,③ cat(1) 是免费的,因为 grep(1) 很乐意接受 FILE 参数。
  • 感谢您的回复,退货支票确实如我所愿。您能否详细说明为什么它会失败?有没有更优雅的方式来运行这个检查?我正在重写grep(1) 行以包含该文件并删除无偿的cat(1) 谢谢。
  • if (system(...)) 条件只有在 system() 报告失败时才会为真,这似乎与您想要的相反。

标签: regex perl file-io duplicates


【解决方案1】:

我不打算评论你剧本其余部分的智慧,但你有:

if (system ("cat","/etc/rc.conf","|","grep","interface")){ 

system 成功返回0

因此,只有当 system 调用失败时,您才会进入该块。

事实上,我现在在没有 /etc/rc.conf 的 Windows 系统上(但 catgrep 感谢 Cygwin。运行以下脚本:

#!/usr/bin/env perl

use strict; use warnings;

if (system ("cat","/etc/rc.conf","|","grep","interface")){
    print "*** it worked! ***\n";
    if ($? == -1) {
        print "failed to execute: $!\n";
    }
    elsif ($? & 127) {
        printf "child died with signal %d, %s coredump\n",
            ($? & 127),  ($? & 128) ? 'with' : 'without';
    }
    else {
        printf "child exited with value %d\n", $? >> 8;
    }
}

产生输出:

cat: /etc/rc.conf: 没有这样的文件或目录
cat: |: 没有这样的文件或目录
cat: grep: 没有这样的文件或目录
cat:接口:没有这样的文件或目录
*** 成功了! ***
孩子以值 1 退出

这意味着system 返回了一个失败代码。现在,如果你想使用 shell 管道和重定向,你应该传递 system 一个字符串,而不是一个列表,然后像这样检查:

if (system ('cat /etc/rc.conf | grep interface') == 0) {

另一方面,我宁愿不相信 shell 传播退出状态。

以下内容应为您指明更好的方向:

#!/usr/bin/env perl

use strict;use warnings;

my %lookup = (
    eth0 => {
        address => '1.1.1.1',
        broadcast => '2.2.2.2',
        netmask => '3.3.3.3',
        gateway => '4.4.4.4',
    },
    wlan0 => {
        address => '5.5.5.5',
        broadcast => '6.6.6.6',
        netmask => '7.7.7.7',
        gateway => '8.8.8.8',
    },

);

while (my $line = <DATA>) {
    if (my ($interface) = ($line =~ /^interface=(\S+)/)) {
        print $line;
        if (exists $lookup{$interface}) {
            $line = process_interface(\*DATA, $lookup{$interface});
            redo;
        }
    }
    else {
        print $line;
    }
}

sub process_interface {
    my ($fh, $lookup) = @_;
    my $keys = join '|', sort keys %$lookup;

    while (my $line = <DATA>) {
        $line =~ s/\A($keys)=.+/$1=$lookup->{$1}/
            or return $line;
        print $line;
    }

    return;
}

__DATA__
#
# /etc/rc.conf - Main Configuration for Arch Linux

. /etc/archiso/functions

# stuff

interface=eth0
address=192.168.0.99
netmask=255.255.255.0
broadcast=192.168.0.255
gateway=192.168.0.1
interface=wlan0
address=192.168.0.99
netmask=255.255.255.0
broadcast=192.168.0.255
gateway=192.168.0.1

输出:

# # /etc/rc.conf - Arch Linux 的主要配置 . /etc/archiso/functions # 东西 接口=eth0 地址=1.1.1.1 网络掩码=3.3.3.3 广播=2.2.2.2 网关=4.4.4.4 接口=wlan0 地址=5.5.5.5 网络掩码=7.7.7.7 广播=6.6.6.6 网关=8.8.8.8

【讨论】:

  • 感谢您的回复,稍后我将详细了解您的脚本的运行情况。我是 perl 的新手并订购了几本书,但我现在在海外服务,这些书需要几个月的时间才能到达这里。 if (system ("cat","/etc/rc.conf","|","grep","interface")){ 行实际上按我的预期工作。有没有更好的方法来查看 'interface' 是否已经在 rc.conf 文件中分配了一个值?
  • @Gosu 我对system 的事情感到困惑。我现在在 Windows 系统上,所以我无法检查,但如果调用成功,它不应该进入循环体,因为system 肯定会在成功时返回0。您是否尝试使用其中没有单词interface 的文件?我实际上会通过寻找直到找到接口将其合并到脚本中。我会更新我的示例。
  • 在我的系统上,system() 调用 cat,该实用程序尝试打开一个名为“/etc/rc.conf”的文件、一个名为“|”的文件、一个名为“grep”的文件,以及一个名为“interface”的文件。
  • @pilcrow 是的,我也发现了这一点并现在将其合并到我的帖子中。
【解决方案2】:

问题是你的if/if/if/if/if/else链,应该是if/elsif/@9876543329@/@98764 @/elsif/else 链。 else { print OS $_ } 在与gateway= 不匹配的每一行上触发,包括与interfaceaddress 等匹配的行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-04
    • 1970-01-01
    • 2013-01-25
    • 1970-01-01
    • 2014-06-17
    • 1970-01-01
    • 2015-05-14
    • 1970-01-01
    相关资源
    最近更新 更多