0

当我运行以下脚本时,我得到一个损坏的 yaml 文件,如下所示

--- 
1: 
  name1: abc
  name2: abc
---
me3: abc
---

问题

谁能看到我做错了?

#!/usr/bin/perl

use strict;
use YAML::Syck;
use Fcntl ':flock', 'SEEK_SET';
use warnings;
use Data::Dumper;

my $acc;
my $acc_fh;

$acc->{1}{name1} = "abc";

unlink 'test.yaml';

# write initial
open F, '>', 'test.yaml';
print F YAML::Syck::Dump($acc);
close F;


($acc, $acc_fh) = read_yaml_with_lock('test.yaml');
$acc->{1}{name2} = "abc";
$acc->{1}{name3} = "abc";
write_yaml_with_lock($acc, $acc_fh);

($acc, $acc_fh) = read_yaml_with_lock('test.yaml');
delete $acc->{1}{name3};
write_yaml_with_lock($acc, $acc_fh);


sub read_yaml_with_lock {
    my ($file) = @_;

    open my $fh, '+<', $file or die $!;
    flock($fh, LOCK_EX) or die $!;

    my $obj = YAML::Syck::LoadFile($fh); # this dies on failure
    return ($obj, $fh);
}

sub write_yaml_with_lock {
    my ($obj, $fh) = @_;

    my $yaml = YAML::Syck::Dump($obj);
    $YAML::Syck::ImplicitUnicode = 1;
    seek $fh, 0, SEEK_SET;   # seek back to the beginning of file

    print $fh $yaml . "---\n";
    close $fh;
}
4

2 回答 2

3

您两次写入同一个文件。第二次编写的 YAML 代码比第一次短,因为您在调用之间删除了该哈希键。但是,您既不unlink是第一次之后的文件,也不truncate是第二次写入文件后的文件。所以你看到的损坏是第一次写入但第二次没有被覆盖的文件部分。

于 2012-09-05T14:08:32.443 回答
3

"me3"部分是剩下的" name3"部分,它被"---\n"(4 个字符)部分覆盖。当你第一次写的时候,你有更多的数据。然后你倒回文件句柄位置并写一个较短的数据,这不会覆盖所有旧的。

我认为你的解决方案“应该”是跳过这个传递文件句柄并倒带它,而是open为每个子例程使用适当的。例如:

sub read_yaml {
    my $file = shift;
    open my $fh, '<', $file or die $!;
    ...
    close $fh;
}

sub write_yaml {
    my ($file, $obj) = @_;
    open my $fh, '>', $file or die $!;
    ...
    close $fh;
}

在操作之间保持文件句柄打开并不是那么有用或高效,并且会带来一些困难。

于 2012-09-05T14:11:51.303 回答