【问题标题】:Perl not interpreting value in ticks, calling it undefined valuePerl 不解释刻度中的值,称其为未定义值
【发布时间】:2014-06-16 22:54:55
【问题描述】:

我正在编写一个子程序,它使用XML::Simple 模块从配置文件中提取信息。

XML 文件具有以下结构

<main>
  <server hostname="blahblah" ... more_attributes="more"/>
  <server etc./>
  <server etc./>
</main>

代码运行良好。它按预期将 XML 数据放入嵌套散列中。但是,当我想使用当前系统的 hostname 将一台服务器与其他服务器隔离时,我遇到了问题。

我认为这条线

my %systemHash = %{$xmlObject->{SERVER}->{`hostname`}};

应该将当前机器的主机名插入到最后一组括号中。但是当执行我得到的代码时

Can't use an undefined value as a HASH reference

【问题讨论】:

  • 能分享一下读取XML的代码吗?有几个选项可以改变 XML::Simple 的行为。
  • 澄清一下:使用默认值,XMLin 给我一个哈希数组的哈希,而不是嵌套的哈希。
  • @JimDavis:最重要的是遵守XML::Simple 模块的作者格兰特麦克莱恩的意愿,他说“在新代码中使用这个模块不鼓励”
  • 顺便说一句,Perl 核心模块Sys::Hostname 导出本机函数hostname,无需使用反引号或外部命令即可工作

标签: xml perl xml-simple


【解决方案1】:

首先,不要使用XML::Simple。它自己的文档说明了这一点

不鼓励在新代码中使用此模块。其他模块也可以提供更直接和一致的接口。特别是,强烈推荐使用 XML::LibXML。

这个模块的主要问题是大量的选项以及这些选项交互的任意方式 - 通常会产生意想不到的结果。

您还应该使用Data::Dump 检查任何子例程调用或shell 命令的结果。它看起来像这样

perl -MData::Dump -E'dd `hostname`'

在我的系统上显示

"Samurai-U\n"

希望您现在看到问题了吗?反引号返回的字符串有一个尾随换行符,并且您的 $xmlObject 哈希中有一个元素具有这样的键。你可以修复它

chomp(my $host = `hostname`)

之后你可以写

my %systemHash = %{ $xmlObject->{SERVER}{$host} }

最后,像这里一样复制所有第一级哈希是很浪费的

my %systemHash = %{$xmlObject->{SERVER}->{`hostname`}}

你不会继续展示你想用这个信息做什么,但一般来说,提取散列reference要好得多,使用

chomp( my $hostname = `hostname` );
my $systemHash = $xmlObject->{SERVER}{$hostname};

更新

使用适当的 XML 解析模块会好得多。

这是一个使用XML::LibXML的示例解决方案

use strict;
use warnings;
use 5.010;    # For 'say'

use XML::LibXML;

my ($xml_file) = @ARGV;

my $xml = XML::LibXML->load_xml(location => $xml_file);

my @servers      = $xml->findnodes('/main/server');
my @server_names = map $_->findvalue('@hostname'), @servers;

say "- $_" for @server_names;

输入文件

<main>
  <server hostname="server1" more_attributes="more"/>
  <server hostname="server2" more_attributes="more"/>
  <server hostname="server3" more_attributes="more"/>
</main>

输出

- server1
- server2
- server3

【讨论】:

  • 感谢 perl -MData::Dump -E'dd hostname' 示例。我从来没有遇到过,非常有帮助。
【解决方案2】:

您应该使用Data::Dumper 转储XML::Simple的输出,如下所示:

use Data::Dumper;
# Retrieve your data structure via XML::Simple. Then...

print Dumper $xmlObject;

您会看到它没有创建您期望的结构,这就是您收到Can't use an undefined value as a HASH reference 消息的原因。该消息意味着$xmlObject 未定义,或者$xmlObject-&gt;{SERVER} 未定义。 perldiag 将错误描述为:

用作硬引用或符号引用的值必须 是一个定义的值。这有助于隐藏一些隐蔽的错误。

您将未定义的值视为哈希引用。如前所述,原因可能是生成的 XML::Simple 结构与您的预期不同。

XML::Simple 并不像它的名字所暗示的那样简单,也不像人们希望的那样简单。 目前它自己的文档不鼓励使用它。有一些配置选项可以帮助规范它产生的结构,但是没有看到更多的 XML,以及你用来阅读它的代码,这就是我能给出的尽可能详细的解释。关于如何解析 XML 的最佳建议是使用更可靠的模块。 XML::TwigXML::LibXML 是两个非常常见且备受推崇的替代方案。

您的代码中还有一个错误,接下来您会遇到:系统调用“hostname”返回的主机名末尾有一个换行符,因此您要求一个哈希键名为:

"somehost\n"

...当您真正想要“somehost”时。我怀疑你没有指望这一点。最低限度,你需要这样的东西:

chomp( my $h = `hostname` );
my %systemHash = %{$xmlObject->{SERVER}->{$h}};

或者这个:

use Sys::Hostname;
my %systemHash = %{$xmlObject->{SERVER}->{hostname()};

我更喜欢后者,因为它更便携,模块Sys::Hostname comes with Perl,所以你的系统上已经有了它。它的输出不需要咀嚼。

【讨论】:

  • 感谢@DavidO 的回答。虽然其他人可能提供了类似的信息,但您回答了我提出的实际问题并提出了另一种选择。在这种情况下使用Sys::Hostnamehostname() 解决了我的问题。关于XML::LibXML 的建议很好。在进一步查看模块后,我认为这对于我需要的非常简单的解析来说有点矫枉过正。再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-09
相关资源
最近更新 更多