Разделить один файл на несколько файлов на основе шаблона

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

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

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

Мой желаемый результат состоит в том, чтобы иметь несколько файлов (в зависимости от количества eyecatchers в моем временном файле).

Итак, мой вывод будет выглядеть примерно так:

$cat temp1
3d3d01f87347545002f1d5b2be4ee4d700010100018000cc57e582000000000000000
0000000000087d3f513000000000000000000000000000000000001001001010f00000000
002658783100b354c52658783100b4

$cat temp2
3d3d0000ad6413400103231665f301010b9130194899f2ffffffffffff02007c00dc0
15800a040402802f1d5b2b8ca5674504f4330310000000000046363070000000000000000
000000000065450000b4fb6b400039

$cat temp3
3d3d1116cdcc57e58287d3f55285a1084b

Ответы

Ответ 1

#!/usr/bin/perl

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

for $match (split(/(?=3d3d)/)) {
      open(O, '>temp' . ++$n);
      print O $match;
      close(O);
}

Ответ 2

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

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

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

cat temp |
  awk -v RS="3d3d" 'NR > 1 { print RS $0 > "temp" (NR-1); close("temp" (NR-1)) }' 

Ответ 3

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

# 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/' {*}

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

Ответ 4

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 an extra since concatted-file.txt ends in a NULL character.
# awk: does a bunch of stuff as the final command. Remember it 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


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

Ответ 5

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

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

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

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

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

НТН