3

У меня есть два файла, file1.txt и file2.txt , file1.txt :

perimeter a=10
perimeter b=15
perimeter c=20

file2.txt это:

perimeter a=12
perimeter b=14

Дайте мне знать sed или Perl, чтобы переопределить значения в file1.txt

Вывод file1.txt должен выглядеть так:

perimeter a=12
perimeter b=14
perimeter c=20

Я использовал ряд команд, как

sed -i -e '/parameter//d r file1.txt' file2.txt
awk -F, 'NR==FNR{a[$1]=$0;next;}a[$1]{$0=a[$1]}1' file1.txt file2.txt
awk -F, 'NR==FNR{a[$1]=$0;next;}a[$1]{$0=a[$1]}1' file2.txt file1.txt

Ничто из этого не помогает.

2 ответа2

1

Может быть как то так? Сохраните это как, например, script.pl и запустите через perl script.pl file1.txt file2.txt и вы получите желаемый результат. Я сделал несколько предположений относительно формата файла, например, нет пустых строк или комментариев, которые нужно сохранять.

#!/usr/bin/env perl
use warnings;
use strict;
use Tie::IxHash;

tie my %hash, 'Tie::IxHash';
while (<>) {
    chomp;
    my ($k,$v) = split /=/, $_, 2;
    $hash{$k} = $v;
}
for my $k (keys %hash) {
    print "$k=$hash{$k}\n";
}

Обновление: версия без зависимости от Tie::IxHash:

use warnings;
use strict;

my (%hash, @keys);
while (<>) {
    chomp;
    my ($key,$value) = split /=/, $_, 2;
    push @keys, $key unless exists $hash{$key};
    $hash{$key} = $value;
}
for my $key (@keys) {
    print "$key=$hash{$key}\n";
}

Это также можно засунуть в лайнер:

perl -F= -anle 'exists$h{$F[0]}||push@k,$F[0];$h{$F[0]}=$F[1];END{print"$_=$h{$_}"for@k}' file1.txt file2.txt

Однако обратите внимание, что в отличие от скрипта, этот oneliner не будет работать правильно, если есть строки с более чем одним = .

0

Вы должны быть немного осторожны с тем, как на самом деле переписать файл. Не делай этого:

some_program < file1 > file1

Поскольку оболочка заботится о перенаправлениях перед запуском программы, перенаправление > обрезает файл, прежде чем программа получит возможность прочитать его.

Вот несколько альтернатив:

  1. явно записать во временный файл, а затем переписать оригинал

    tmpfile=$(mktemp)
    some_program < file1 > "$tmpfile" && mv "$tmpfile" file1
    

    Используйте && чтобы вы только mv если программа успешно завершена.

  2. используйте команду sponge из пакета moreutils

    some_program < file1 | sponge file1
    
  3. Если вы используете GNU awk, вы можете использовать расширение -i inplace , но вам нужно немного настроить программу:

    gawk -i inplace -F= '
        NR == FNR {a[$1] = $0; print; next} 
        $1 in a {$0 = a[$1]} 
        1
    ' file{2,1}.txt
    

Опции 2 и 3 также используют временный файл, но позаботятся о деталях за вас.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .