Разделить один файл на несколько файлов на основе шаблона - PullRequest
17 голосов
/ 09 ноября 2011

У меня есть двоичный файл, который я конвертирую в обычный файл с помощью hexdump и нескольких команд awk и sed. Выходной файл выглядит примерно так -

$cat temp
3d3d01f87347545002f1d5b2be4ee4d700010100018000cc57e5820000000000000000000
000000087d3f513000000000000000000000000000000000001001001010f000000000026 
58783100b354c52658783100b43d3d0000ad6413400103231665f301010b9130194899f2f
fffffffffff02007c00dc015800a040402802f1d5b2b8ca5674504f433031000000000004
6363070000000000000000000000000065450000b4fb6b4000393d3d1116cdcc57e58287d
3f55285a1084b

В временном файле есть несколько бросающихся в глаза (3d3d), которые не повторяются так часто. Они как бы обозначают начало новой двоичной записи. Мне нужно разделить файл на основе этих бросающихся в глаза.

Мой желаемый вывод - иметь несколько файлов (в зависимости от количества приманок для глаз в моем временном файле).

Так что мой вывод будет выглядеть примерно так -

$cat temp1
3d3d01f87347545002f1d5b2be4ee4d700010100018000cc57e582000000000000000
0000000000087d3f513000000000000000000000000000000000001001001010f00000000
002658783100b354c52658783100b4

$cat temp2
3d3d0000ad6413400103231665f301010b9130194899f2ffffffffffff02007c00dc0
15800a040402802f1d5b2b8ca5674504f4330310000000000046363070000000000000000
000000000065450000b4fb6b400039

$cat temp3
3d3d1116cdcc57e58287d3f55285a1084b

Ответы [ 5 ]

18 голосов
/ 09 ноября 2011

Переменная RS в awk хороша для этого, позволяя вам определить разделитель записей.Таким образом, вам просто нужно захватить каждую запись в своем собственном временном файле.Простейшая версия:

cat temp |
  awk -v RS="3d3d" '{ print $0 > "temp" NR }' 

Образец текста начинается с броска в глаза 3d3d, поэтому temp1 будет пустым файлом.Кроме того, сама привлекательность не будет в начале временных файлов, как было показано для временных файлов в вопросе.Наконец, если записей много, вы можете столкнуться с системным ограничением для открытых файлов.Некоторые незначительные осложнения приблизят его к тому, что вы хотите, и сделают его более безопасным:

cat temp |
  awk -v RS="3d3d" 'NR > 1 { print RS $0 > "temp" (NR-1); close("temp" (NR-1)) }' 
14 голосов
/ 09 ноября 2011
#!/usr/bin/perl

undef $/;
$_ = <>;
$n = 0;

for $match (split(/(?=3d3d)/)) {
      open(O, '>temp' . ++$n);
      print O $match;
      close(O);
}
5 голосов
/ 09 ноября 2011

Это может сработать:

# sed 's/3d3d/\n&/2g' temp | split -dl1 - temp
# ls
temp temp00  temp01  temp02
# cat temp00
3d3d01f87347545002f1d5b2be4ee4d700010100018000cc57e5820000000000000000000000000087d3f513000000000000000000000000000000000001001001010f000000000026 58783100b354c52658783100b4
# cat temp01
3d3d0000ad6413400103231665f301010b9130194899f2ffffffffffff02007c00dc015800a040402802f1d5b2b8ca5674504f4330310000000000046363070000000000000000000000000065450000b4fb6b400039
# cat temp02
3d3d1116cdcc57e58287d3f55285a1084b

EDIT:

Если в исходном файле есть новые строки, вы можете сначала удалить их, используя tr -d '\n' <temp, а затем направить вывод через указанную выше команду sed. Однако, если вы хотите сохранить их, то:

 sed 's/3d3d/\n&/g;s/^\n\(3d3d\)/\1/' temp |csplit -zf temp - '/^3d3d/' {*}

Должен сделать трюк

0 голосов
/ 06 июня 2019

Mac OS X ответ

Где этот хороший awk -v RS="pattern" трюк не работает. Вот что у меня получилось:

Учитывая этот пример concatted.txt

filename=foo bar
foo bar line1
foo bar line2
filename=baz qux
baz qux line1
baz qux line2

используйте эту команду (удалите комментарии, чтобы избежать сбоя)

# cat: useless use of cat ^__^;
# tr: replace all newlines with delimiter1 (which must not be in concatted.txt) so we have one line of all the next
# sed: replace file start pattern with delimiter2 (which must not be in concatted.txt) so we know where to split out each file
# tr: replace delimiter2 with NULL character since sed can't do it
# xargs: split giant single-line input on NULL character and pass 1 line (= 1 file) at a time to echo into the pipe
# sed: get all but last line (same as head -n -1) because there's an extra since concatted-file.txt ends in a NULL character.
# awk: does a bunch of stuff as the final command. Remember it's getting a single line to work with.
#   {replace all delimiter1s in file with newlines (in place)}
#   {match regex (sets RSTART and RLENGTH) then set filename to regex match (might end at delimiter1). Note in this case the number 9 is the length of "filename=" and the 2 removes the "§" }
#   {write file to filename and close the file (to avoid "too many files open" error)}
cat ../concatted-file.txt \
| tr '\n' '§' \
| sed 's/filename=/∂filename=/g' \
| tr '∂' '\0' \
| xargs -t -0 -n1 echo \
| sed \$d \
| awk '{match($0, /filename=[^§]+§/)} {filename=substr($0, RSTART+9, RLENGTH-9-2)".txt"} {gsub(/§/, "\n", $0)} {print $0 > filename; close(filename)}'

приводит к этим двум файлам с именами foo bar.txt и baz qux.txt соответственно:

filename=foo bar
foo bar line1
foo bar line2


filename=baz qux
baz qux line1
baz qux line2


Надеюсь, это поможет!

0 голосов
/ 09 ноября 2011

Это зависит от того, есть ли в вашем файле temp одна строка или нет. Но если предположить, что это одна строка, вы можете перейти с:

sed 's/\(.\)\(3d3d\)/\1#\2/g' FILE | awk -F "#" '{ for (i=1; i++; i<=NF) { print $i > "temp" i } }' 

Первый sed вставляет # в качестве разделителя поля / записи, затем awk разделяется на # и печатает каждое "поле" в свой собственный файл.

Если входной файл уже разделен на 3d3d, вы можете перейти с:

awk '/^3d3d/ { i++ } { print > "temp" i }' temp

НТН

...