Это выполнит то, что Вы спрашиваете:
awk -F';' '{print $1 ";" $2 ";" $3;}' <input >output
awk
утилита хорошо разработана для этой задачи. Это может легко сократить отдельные строки в поля, затем управлять ими на основе этого. -F';'
аргумент говорит awk
использовать ;
как разделитель полей. Кавычки необходимы, потому что оболочка интерпретировала бы ;
как разделитель команды без них.
Команда, данная awk для выполнения для каждой строки ( '{print $1 ";" $2 ";" $3;}'
бит), так же заключается в кавычки для хранения всех забавных символов ({
, }
, $
, "
и ;
в этом случае) от того, чтобы быть рассматриваемым особенно оболочкой и удостоверяются, что все это передается awk
как одна единица.
И, конечно, <input
и >output
директивы перенаправления, даваемые оболочке для перенаправления ввода и вывода команды от и до файла.
Как насчет grep
grep -oE 'A1.{10}|B1.{4}|C1.{7}' input.txt
Это печатает каждую запись каждого типа записи на отдельной строке. Перенаправить grep
вывод в 3 файла называют A1
, B1
, C1
соответственно,
grep -oE 'A1.{10}|B1.{4}|C1.{7}' input.txt|
awk -v OFS= -v FS= '{f=$1$2; $1=$2=""; print>f}'
Вот возможное решение с помощью FPAT простофили
BEGIN {
FPAT="A1.{10}|B1.{4}|C1.{7}" #define field contents
}
{
for(i=1;i<=NF;i++)
print $i >> substr($i,0,2) #print the field to file A1,B1,etc
}
Как острота:
gawk 'BEGIN{FPAT="A1.{10}|B1.{4}|C1.{7}"} {for(i=1;i<=NF;i++)print $i >> substr($i,0,2)}' < datafile
FPAT
требует версии 4 простофили. См.: linuxjournaldigital.com/linuxjournal/201109#pg98
– Håkon Hægland
24.12.2013, 21:44
В Perl:
#!/usr/bin/env perl
use strict;
use warnings;
use re qw(eval);
my %field_widths = (
A1 => 10,
B1 => 4,
C1 => 7,
#...(fill this up with the widths of your 38 record types)
);
# Make a regex of record types; sort with longest first as appropriate for
# ... regex alternation:
my $record_type_regex = join '|', sort { length($b) <=> length($a) } keys %field_widths;
my %records;
my $marker_length=7; #Assuming the marker is 7 characters long
while(<>){
chomp;
while( # Parse each line of input
m!
(.{$marker_length}) # Match the record marker (save in $1)
($record_type_regex) # Match any record type (save in $2)
(
(??{'.'x$field_widths{$2})} # Match a field of correct width
) # Save in $3
!xg){
$records{$2}.="$1$2$3\n";
}
}
for my $file (sort keys %records){
open my $OUT,'>',$file or die "Failed to open $file for writing: $!\n";
print $OUT $records{$file};
close $OUT
}
Вызовите его как:
[user@host]$ ./myscript.pl file_of_data
Код протестирован и работы с Вашим данным входом.
Обновление
В Ваших комментариях Вы запросили "Unix, эквивалентный" вышеупомянутого. Я высоко сомневаюсь, там существует такая вещь, так как выражение Perl, используемое для парсинга строки, является очень неправильным выражением, и я сомневаюсь, что ванильные регулярные выражения могут проанализировать формат определенных данных: это слишком подобно известному типу выражения, которое regex не может проанализировать (соответствуйте любому количеству a
сопровождаемый тем же количеством b
).
В любом случае самый близкий подход "Unix", который я могу найти, является обобщением 1_CR's ответ. Необходимо отметить, что этот подход характерен для реализации GNU grep
и поэтому не будет работать над большинством Нельдов. Подход Perl, наоборот, должен работать то же над любой платформой, что Perl продолжает работать. Вот мой предложенный GNU grep
подход:
cat <<EOF \
| while read -r record width;do
grep -oE ".{7}$record.{$width}" input_file\ #replace 7 with marker length
>> "$record"
done
A1 10
B1 4
# enter your 38 record types
EOF
Обновление
На основе запросов OP в комментариях, вместо того, чтобы передать имя файла как параметр командной строки, это может быть открыто в рамках сценария как так:
open my $IN,'<',$input_file_name or die "Failed to open $input_file: $!\n";
while(<$IN>){ #instead of while(<>)
...
Это предполагает объявление переменной $input_file_name
содержать, ну, в общем, входное имя файла.
Что касается добавления метки времени к имени выходного файла, можно использовать qx{}
синтаксис: между фигурными скобками можно поместить любую команду Unix, которую Вы хотите, и она будет выполнена и ее чтение стандартного вывода назад вместо qx{}
оператор:
open my $OUT,'>',"$file_".qx{date +%Y-%m-%d--%I:%M:%S%P}
qx
оператор не ограничивается фигурными скобками, используйте свой любимый символ в качестве разделителя, просто удостоверьтесь, что это не находится в команде, которую необходимо выполнить:
qx<...>
qx(...)
qx!...!
qx@...@
и так далее...
В некотором коде Perl можно видеть обратные галочки (` `
) используемый для выполнения этой функции, вместо этого, подобной тому, что делает оболочка. Просто думайте qx
оператор как обобщение обратных галочек к любому разделителю.
Между прочим, это даст немного отличающуюся метку времени каждому файлу (если различием их времен создания, окажется, будет конечное число секунд). Если Вы не хотите это, можно сделать это на двух шагах:
my $tstamp = qx{...};
open my $OUT,'>',"$file_$tstamp" or die...;