mor*_*o11 5 grep bash sed shell-script regular-expression
我有以下情况,有两个文件。
文件1
not relevant = does not matter
some stuff
# var1=1
# var 2 = 2
# var3 = 3
some stuff
Run Code Online (Sandbox Code Playgroud)
文件2
some other stuff
# does not matter either
# var1=a
# var 2 = b
# var3 = c
some other stuff
Run Code Online (Sandbox Code Playgroud)
我想使用 bash 脚本从以相应变量及其值开头#并包含=相应变量及其值的所有行中提取,收集所有内容并将其写入新文件
var1,var 2,var3
1,2,3
a,b,c
Run Code Online (Sandbox Code Playgroud)
变量名称可以包含空格。前后也=可能出现空格。右侧的值不包含空格。如果出现空格,两个文件中都会出现空格。
这是一项需要文本处理语言(例如 awk 或 perl)的任务,而不是 shell 脚本。
$ cat vars2csv.pl
#!/usr/bin/perl
use strict;
# %vars is a Hash-of-Hashes (HoH) where the primary keys
# are the filenames, and each element is a hash containing
# each "variable" name found in in the input and its
# corresponding value. See man pages for perldata and perldsc.
my %vars;
# Array @fields and hash %seen to keep track of new
# "variable" names in the order we see them.
my @fields;
my %seen;
# Keep a copy of the arguments so we can output the data in
# the same order we read them.
my @files = @ARGV;
while (<>) {
chomp;
next unless /^#.*=/;
s/^#\s*//;
my ($key,$val) = split /\s*=\s*/, $_, 2;
if (!defined($seen{$key})) {
push @fields, $key;
$seen{$key} = 1;
};
# $ARGV is the name of the current file being read
# by the `while(<>)` loop.
$vars{$ARGV}{$key} = $val;
};
print join(",", @fields), "\n";
foreach my $f (@files) {
next unless -r $f; # skip output for filenames that weren't readable
print join(",", @{$vars{$f}}{@fields}), "\n";
};
Run Code Online (Sandbox Code Playgroud)
该脚本会跟踪读取文件的顺序以及查看字段名称的顺序,因为 Perl 哈希本质上是无序的(这对于大多数语言中关联数组的大多数实现来说很常见)。我可以编写它来在输出阶段对键进行排序(perl 有一个非常有用的内置sort函数),所以至少它们会以可预测的顺序输出,但我认为最好使用一些变量来记住原始订单。
它适用于任意数量的输出字段,并且不关心字段名称或值是什么。在匹配行中,任何前导空格之后和第一个=符号之前的所有内容都是“键”,第一个=符号之后的所有内容都是值。周围的空格=不包含在键或值中(该行是在 上分割的,\s*=\s*而不仅仅是=)。perldoc -f split有关 split 函数的详细信息,请参阅。
如果给定的键在文件中出现多次,则最后一次出现的值将是输出的值。如果您希望它保留第一个并忽略任何后续事件,请在该行之前$vars{$ARGV}{$key} = $val;添加以下行:
next if (defined($vars{$ARGV}{$key}));
Run Code Online (Sandbox Code Playgroud)
示例运行:
$ chmod +x ./vars2csv.pl
$ ./vars2csv.pl file1 file2
var1,var 2,var3
1,2,3
a,b,c
Run Code Online (Sandbox Code Playgroud)
值得注意的是:此脚本会忽略所有不以 a 开头#且包含=. 这意味着它会处理与该条件匹配的所有行 - 包括任何碰巧包含=您不打算定义变量的注释行。根据输入文件中的具体内容,这可能是一个需要修复的错误(通过找出一种模式来排除这些不需要的行,或者通过设计一个更好的模式来仅匹配想要的行)。
顺便说一句,我next unless -r $f;在脚本中添加了该行,因为我使用不存在的文件名参数和阻止读取它们的权限测试了脚本。发生此类错误时,Perl 会打印一条警告消息,但脚本会打印一行,其中包含用逗号分隔的空字段。该行阻止该输出。
对于不包含任何注释的可读文件,该脚本还将打印一行用逗号分隔的空字段var=value。如果您也想阻止这些文件的输出,请在该行之前print join...添加以下内容。
next unless (keys %{ $vars{$f} }); # skip output for files with NO key=val comments
Run Code Online (Sandbox Code Playgroud)
包含某些但不是所有字段的文件将打印其所拥有字段的正确值和任何缺失字段的空值。例如,仅包含的文件# var1=1将打印1,,为输出行。如果您想跳过这些文件的输出:
next unless (@{$vars{$f}}{@fields}); # skip output for files missing ANY key
Run Code Online (Sandbox Code Playgroud)