Думаю, это работает:
$ grep -i 'name="andy" remote="origin" branch=".*\"' <filename> | awk -F' ' '{print $5}' | sed -E 's/branch=\"(.*)\"/\1/'
master
Часть awk
гарантирует, что возвращается только branch="master"
, часть sed
возвращает то, что находится между двойными кавычками со ссылкой (, \1
соответствует части, заключенной в круглые скобки ).
Теперь я знаю, что здесь много людей с гораздо большими знаниями об искусстве awk и sed, так что я готов к некоторой критике:-)
Использование любого awk в любой оболочке на каждом сервере Unix:
$ cat tst.awk
BEGIN {
numTags = split("Name City Age Couse",nums2tags)
for (tagNr=1; tagNr<=numTags; tagNr++) {
tag = nums2tags[tagNr]
tags2nums[tag] = tagNr
wids[tagNr] = ( length(tag) > length("null") ? length(tag) : length("null") )
}
OFS=" | "
}
(NR==1) || (prevTag=="Couse") {
numRecs++
}
{
gsub(/^"|"$/,"")
tag = val = $0
sub(/".*/,"",tag)
sub(/[^"]+":"/,"",val)
tagNr = tags2nums[tag]
vals[numRecs,tagNr] = val
wid = length(val)
wids[tagNr] = ( wid > wids[tagNr] ? wid : wids[tagNr] )
prevTag = tag
}
END {
# Uncomment these 3 lines if youd like a header line printed:
# for (tagNr=1; tagNr<=numTags; tagNr++) {
# printf "%-*s%s", wids[tagNr], nums2tags[tagNr], (tagNr<numTags ? OFS : ORS)
# }
for (recNr=1; recNr<=numRecs; recNr++) {
for (tagNr=1; tagNr<=numTags; tagNr++) {
val = ( (recNr,tagNr) in vals ? vals[recNr,tagNr] : "null" )
printf "%-*s%s", wids[tagNr], val, (tagNr<numTags ? OFS : ORS)
}
}
}
$ awk -f tst.awk file
asxadadad ,aaf dsf | Mum | 23 | BBS
null | Ors | 11 | MB
adad sf | Kol | 21 | BB
pqr | null | 21 | NN
или если вы не хотите использовать жесткий -закодированный список тегов (имена полей/столбцов):
$ cat tst.awk
BEGIN { OFS=" | " }
(NR==1) || (prevTag=="Couse") {
numRecs++
}
{
gsub(/^"|"$/,"")
tag = val = $0
sub(/".*/,"",tag)
sub(/[^"]+":"/,"",val)
if ( !(tag in tags2nums) ) {
tagNr = ++numTags
tags2nums[tag] = tagNr
nums2tags[tagNr] = tag
wids[tagNr] = ( length(tag) > length("null") ? length(tag) : length("null") )
}
tagNr = tags2nums[tag]
vals[numRecs,tagNr] = val
wid = length(val)
wids[tagNr] = ( wid > wids[tagNr] ? wid : wids[tagNr] )
prevTag = tag
}
END {
for (tagNr=1; tagNr<=numTags; tagNr++) {
printf "%-*s%s", wids[tagNr], nums2tags[tagNr], (tagNr<numTags ? OFS : ORS)
}
for (recNr=1; recNr<=numRecs; recNr++) {
for (tagNr=1; tagNr<=numTags; tagNr++) {
val = ( (recNr,tagNr) in vals ? vals[recNr,tagNr] : "null" )
printf "%-*s%s", wids[tagNr], val, (tagNr<numTags ? OFS : ORS)
}
}
}
$ awk -f tst.awk file
Name | City | Age | Couse
asxadadad ,aaf dsf | Mum | 23 | BBS
null | Ors | 11 | MB
adad sf | Kol | 21 | BB
pqr | null | 21 | NN
Обратите внимание, что порядок столбцов в выводе для этого второго сценария будет соответствовать порядку, в котором эти теги появляются во входных данных, поэтому им нужна строка заголовка для идентификации значений, если только не гарантируется, что все теги встречаются во входных данных в порядок, в котором вы хотите, чтобы они выводились.
В перл. Я бы добавил больше объяснений того, что он делает/как он работает, но я думаю, что комментарии в коде охватывают все это.
#!/usr/bin/perl
use strict;
my @people; # Array-of-Arrays (AoA) to hold each record
my %person; # hash to hold the current record as it's being read in.
# list of valid field names, in the order you want them printed
my @names = qw(Name City Age Couse);
my $end_key = 'Couse';
# build a regex from the valid names
my $names = join('|',@names);
my $names_re = qr/^(?:$names)$/;
# Initialise field widths, with a minimum of 4 (for 'null').
my %widths = map {$_ => (length > 4 ? length : 4) } @names;
while(<>) {
chomp;
s/^"|"$//g; # strip leading and trailing quotes
my ($key,$val) = split /"?:"?/; # split on :, with optional quotes.
if ($key =~ m/$names_re/) {
$widths{$key} = length($val) if ($widths{$key} < length($val) );
$person{$key} = $val;
if ($key eq $end_key) {
# push an array into the @people array, containing the values of
# the valid fields, in order. Use null as the default value
# if any field is empty/undefined.
push @people, [ map { $person{$_} || 'null' } @names ];
%person = ();
};
} else {
print STDERR "Error on input line $.: unrecognised data\n";
};
};
# build a printf format string, using the longest width of each field.
my $fmt = join(' | ', map { "%-$widths{$_}s" } @names). "\n";
# optional header line, comment out if not wanted
printf $fmt, @names;
# optional ruler line, comment out if not wanted
print join('-|-', map { '-' x $widths{$_} } @names). "\n";
foreach my $p (@people) {
printf $fmt, @{ $p };
}
Сохранить как, например, columns.pl
и сделать исполняемым с помощью chmod +x.
Выход:
$ chmod +x columns.pl
$./columns.pl demo.txt
Name | City | Age | Couse
--------------------|------|------|------
asxadadad ,aaf dsf | Mum | 23 | BBS
null | Ors | 11 | MB
adad sf | Kol | 21 | BB
pqr | null | 21 | NN
Использование массивов bash , в которых мы храним имена полей заголовков. Поскольку код sed повторяется для различных полей заголовка, мы сначала генерируем код sed на основе заголовка. Затем сгенерируйте вывод.
#!/usr/bin/env bash
declare -a tags=(Name City Age Couse end)
{
echo 'H;/^"Couse":/!d;z;x'
paste -d'\n' \
<(printf 's/\\n"%s":"([^"]*)"(.*)/\\2|\\1/\n' "${tags[@]:0:4}") \
<(printf 't %s\n' "${tags[@]:1:4}") \
<(yes 's/$/|null/'|head -n4) \
<(printf ':%s\n' "${tags[@]:1:4}");
echo 's/^\|//'
} |
sed -Ef - demo.txt |
column -ts'|' -o '|' |
sed -e 's/|/ & /g'
Выход:
asxadadad ,aaf dsf | Mum | 23 | BBS
null | Ors | 11 | MB
adad sf | Kol | 21 | BB
pqr | null | 21 | NN
Здесь описан другой метод с использованием утилиты perl .
perl -lne '
@tags = qw(Name City Age Couse) if $. == 1;
%h = (%h, /"([^:]+)":"(.*)"/);
next unless /^"Couse":/;
for (@tags) {
$h{$_} ||= q(null);
length($h{$_})>$maxw{$_} and $maxw{$_}=length($h{$_});
push @{$Aref->[$nr]}, $h{$_};
}
$nr++; %h=();
}{
my $fmt = join "A2", map { sprintf q[A%d], $_+1 } @maxw{@tags};
print pack $fmt, split /([|])/, join q[|], @{shift @$Aref} while @$Aref;
' demo.txt
Использование Raku (, ранее известного как Perl _6)
Ниже приведено довольно быстрое -и -грязное решение на языке программирования Raku. Сохраните приведенный ниже код в файл с расширением .raku
(.p6
, в настоящее время также работает ):
my @a1= $*ARGFILES.IO.lines;
#my @a1= "/Can/use/hardcoded/path/to/file.txt".IO.lines;
@a1.=map(*.split(":"));
my @a2; my $i=0, my $j=0;
my $b = << \"Name\" \"City\" \"Age\" \"Couse\" >> ;
while
$i < (@a1.elems) {
my $tag = $b[$j];
if @a1[$i][0] ~~ / <$tag> / {
#`{above RHS matches inside double quotes}
@a2.push(@a1[$i]);
$i++; $j=(($j+1) mod 4);}
else
{@a2.push((<<$tag>>; <<\"NULL\">>));
$j=(($j+1) mod 4);}
};
.say for @a2;
"____________\n".say;
my @b;
loop (my $n=0; $n < $b.elems; $n++) {
my $tag0 := $b[$n];
@b.push: @a2.grep(/<$tag0>/).[0..*-1]>>.[1];
};
$b.say;.say for @b;
"____________\n".say;
put $b.fmt('%s|').join("\n");
put ([Z] @b)>>.fmt('%s|').join("\n");
Запустите приведенный выше сценарий в командной строке терминала, указав имя вашего файла -из -интереса (, например.~$ raku make_table.p6 input.txt
). Выберите один из трех доступных выходов (, т. е. закомментируйте -два других ). Вывод первой секции (строка .say for @a2;
выше )заполняет значения NULL
для 4 ключей, давая:
("Name" "asxadadad ,aaf dsf")
("City" "Mum")
("Age" "23")
("Couse" "BBS")
("Name" "NULL")
("City" "Ors")
("Age" "11")
("Couse" "MB")
("Name" "adad sf")
("City" "Kol")
("Age" "21")
("Couse" "BB")
("Name" "pqr")
("City" "NULL")
("Age" "21")
("Couse" "NN")
Выход средней секции (между двумя разделителями горизонтальных строк )дает:
("Name" "City" "Age" "Couse")
("asxadadad ,aaf dsf" "NULL" "adad sf" "pqr")
("Mum" "Ors" "Kol" "NULL")
("23" "11" "21" "21")
("BBS" "MB" "BB" "NN")
Вывод нижней секции (ниже второго горизонтального разделителя строк )при передаче через column -ts'\"'
в командной строке bash (с использованием довольно -старой подсистемы unix ), дает:
Name | City | Age | Couse |
asxadadad ,aaf dsf | Mum | 23 | BBS |
NULL | Ors | 11 | MB |
adad sf | Kol | 21 | BB |
pqr | NULL | 21 | NN |
Два основных преимущества использования Raku для решения этой задачи: 1 )расширенная поддержка кавычек и 2 )поддержка Unicode. Добавление Raku в вашу среду программирования позволяет вам сохранить ваши прежние -существующие инструменты Unix/Linux --у вас просто есть дополнительный инструмент, когда он вам нужен.
[Выше протестировано на GNU bash, версия 3.2.57 (1 )-релиз (x86 _64 -яблоко -darwin14 )]
https://raku.org/
Данные выглядят так, как будто они были изменены по сравнению с тем, что изначально было каким-то документом JSON.
Вернем структуру документа JSON,
[{
в начало документа и }]
в конец, },{
в конец каждой строки, начинающейся с точной строки "Couse"
(, но не в последнюю строку ), и sed -e '1 s/^/[{/' -e '$ s/$/}]/' \
-e '/^"Couse"/ { $! s/$/},{/; }' \
-e 's/"$/&,/' file
При красивой -печати наш документ превращается в
[
{
"Name": "asxadadad ,aaf dsf",
"City": "Mum",
"Age": "23",
"Couse": "BBS"
},
{
"City": "Ors",
"Age": "11",
"Couse": "MB"
},
{
"Name": "adad sf",
"City": "Kol",
"Age": "21",
"Couse": "BB"
},
{
"Name": "pqr",
"Age": "21",
"Couse": "NN"
}
]
Затем мы можем преобразовать это в CSV, передав его через jq
(, добавив некоторые заголовки столбцов и заменив пустые значения строкой null
при этом):
jq -r ' [ "Name", "City", "Age", "Couse" ],
(.[] | [.Name, .City, .Age, .Couse ]) |
map(. // "null") | @csv'
Это приведет к
"Name","City","Age","Couse"
"asxadadad ,aaf dsf","Mum","23","BBS"
"null","Ors","11","MB"
"adad sf","Kol","21","BB"
"pqr","null","21","NN"
Затем мы можем использовать csvlook
из инструментария csvkit
для создания красивой таблицы.
Окончательный конвейер будет выглядеть так
sed -e '1 s/^/[{/' -e '$ s/$/}]/' \
-e '/^"Couse"/ { $! s/$/},{/; }' \
-e 's/"$/&,/' file |
jq -r ' [ "Name", "City", "Age", "Couse" ],
(.[] | [.Name, .City, .Age, .Couse ]) |
map(. // "null") | @csv' |
csvlook --blanks
Мы используем csvlook
с его опцией --blanks
, чтобы сохранить строки null
такими, какие они есть (, в противном случае эти )были бы удалены.
Результатом будет
| Name | City | Age | Couse |
| ------------------- | ---- | --- | ----- |
| asxadadad ,aaf dsf | Mum | 23 | BBS |
| null | Ors | 11 | MB |
| adad sf | Kol | 21 | BB |
| pqr | null | 21 | NN |
Или, представленный как уценка:
Имя | Город | Возраст | Коуз |
---|---|---|---|
asxadadad,aaf dsf | Мама | 23 | ББС |
ноль | Орс | 11 | МБ |
Адад СФ | Кол | 21 | ББ |
пгр | ноль | 21 | НН |