Сценарий Sed, отказывающий на большом файле

У меня есть сценарий оболочки, который является в сущности sed сценарием с некоторыми проверками. Цель сценария состоит в том, чтобы преобразовать заголовок файла от.

&FCI
NORB=280,
NELEC=78,
MS2=0,
UHF=.FALSE.,
ORBSYM=1,1,1,1,1,1,1,1,<...>
&END
  1.48971678130072078261E+01   1   1   1   1
 -1.91501428271686324756E+00   1   1   2   1
  4.38796949990802698238E+00   1   1   2   2

кому:

&FCI NORB=280, NELEC=78, MS2=0, UHF=.FALSE., 
ORBSYM=1,1,1,1,1,1,1,1,<...>
ISYM=1,
/
  1.48971678130072078261E+01   1   1   1   1
 -1.91501428271686324756E+00   1   1   2   1
  4.38796949990802698238E+00   1   1   2   2

Это - сценарий:

#!/bin/bash

# $1 : FCIDUMP file to convert from "new format" to "old format"

if [ ${#} -ne 1 ]
then
  echo "Syntaxis: fcidump_new2old FCIDUMPFILE" 1>$2
  exit 1
fi

if egrep '&FCI ([a-zA-Z2 ]*=[0-9 ]*,){2,}' ${1} > /dev/null
then
  echo "The provided file is already in old FCIDUMP format." 1>&2
  exit 2
fi

sed '
1,20 {
   :a; N; $!ba
   s/\(=[^,]*,\)\n/\1 /g
   s/\(&FCI\)\n/\1 /
   s/ORBSYM/\n&/g
   s/&END/ISYM=1,\n\//
}' -i "${1}"

exit 0

Этот сценарий работает на "маленькие" файлы и но теперь я встретился с файлом приблизительно 9 гигабайтов и катастрофическими отказами сценария с "супер четким сообщением об ошибке":

script.sh: line 24: 406089 Killed                  sed '
1,20 {
   :a; N; $!ba
   s/\(=[^,]*,\)\n/\1 /g
   s/\(&FCI\)\n/\1 /
   s/ORBSYM/\n&/g
   s/&END/ISYM=1,\n\//
}' -i "${1}"

Как я могу сделать этот sed сценарий, чтобы действительно только посмотреть на заголовок и смочь обработать такие большие файлы? Ужасный hardcoded "20" является btw там, потому что я не знаю sth лучше.

Дополнительная информация:

  • после попытки некоторых вещей я видел, что это странные файлы было произведено: sedexG4Lg, sedQ5olGZ, sedXVma1Y, sed21enyi, sednzenBn, sedqCeeey sedzIWMUi. Все были пусты кроме sednzenBn, который был похож на входной файл только, но половину из него.

  • отбрасывание флага-i и перенаправление вывода в другой файл дают пустой файл.

6
задан 14 December 2018 в 18:33

2 ответа

Общий метод

  • Можно разделить каждый файл на заголовок и второй файл со строками данных
  • Затем можно легко отредактировать заголовок отдельно с текущей командой sed
  • Наконец можно связать заголовок и файл со строками данных.

Легкие инструменты для управления огромными файлами

Тест

  • Я протестировал с Вашим заголовком и файлом с 1 080 000 000 пронумерованных строк (размер 19 ГиБ), полностью 1 080 000 007 строк, и он работал, выходной файл (с 1 080 000 004 строками) был записан за 5 минут в моем старом hp xw8400 рабочая станция (включая ввод команды для запуска сценария оболочки).

    $ ls -lh --time-style=full-iso huge*
    -rw-r--r-- 1 sudodus sudodus 19G 2018-12-15 19:50:45.278328120 +0100 huge.in
    -rw-r--r-- 1 sudodus sudodus 19G 2018-12-15 19:55:46.808798456 +0100 huge.out
    
  • Большие операции записи были между системным разделом на SSD и разделом данных на жестком диске.

Сценарий оболочки

Вам нужно достаточно свободного пространства в файловой системе, где Вы имеете /tmp для огромного временного файла 'данных', больше чем 9 ГБ согласно Вашему исходному вопросу.

$ LANG=C df -h /tmp
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       106G   32G   69G  32% /

Это может казаться неловким способом сделать вещи, но он работает на огромные файлы, не разрушая инструменты. Возможно, необходимо хранить временный файл 'данных' где-то в другом месте, например, во внешнем диске (но это, вероятно, будет медленнее).

#!/bin/bash

# $1 : FCIDUMP file to convert from "new format" to "old format"

if [ $# -ne 2 ]
then
  echo "Syntaxis: $0 fcidumpfile oldstylefile " 1>&2
  echo "Example:  $0 file.in file.out" 1>&2
  exit 1
fi

if [ "$1" == "$2" ]
then
  echo "The names of the input file and output file must differ"
  exit 2
exit
fi

endheader="$(grep -m 1 -n '&END' "$1" | cut -d: -f1)"
if [ "$endheader" == "" ]
then
  echo "Bad input file: the end marker of the header was not found"
  exit 3
fi
#echo "endheader=$endheader"

< "$1" head -n "$endheader" > /tmp/header
#cat /tmp/header

if egrep '&FCI ([a-zA-Z2 ]*=[0-9 ]*,){2,}' /tmp/header  > /dev/null
then
  echo "The provided file is already in old FCIDUMP format." 1>&2
  exit 4
fi

# run sed inline on /tmp/header 
sed '
{
:a; N; $!ba
s/\(=[^,]*,\)\n/\1 /g
s/\(&FCI\)\n/\1 /
s/ORBSYM/\n&/g
s/&END/ISYM=1,\n\//
}' -i /tmp/header 

if [ $? -ne 0 ]
then
  echo "Failed to convert the header format in /tmp/header"
  exit 5
fi

< "$1" tail -n +$(($endheader+1)) > /tmp/tailer

if [ $? -ne 0 ]
then
  echo "Failed to create the 'data' file /tmp/tailer"
  exit 6
fi

#echo "---"
#cat /tmp/tailer
#echo "---"

cat /tmp/header /tmp/tailer > "$2"

exit 0
4
ответ дан 23 November 2019 в 07:58

sed вероятно, НЕ лучший инструмент для этого, заняться расследованиями perl. Однако Вы могли вновь заявить о проблеме как:

  1. Извлеките Старый Заголовок из гигантского файла данных в собственный файл.

  2. Скорректируйте извлеченный Старый Заголовок, для создания этого Новым Заголовком.

  3. Замените Старый Заголовок Новым Заголовком в гигантском файле данных.

    endheader="$(grep -m 1 -n '&END' "$1" | cut -d: -f1)"
    head -n "$endheader" >/tmp/header
    trap "/bin/rm -f /tmp/header" EXIT
    # do the sed stuff to /tmp/header, I assume it does what you want 
    sed '
    {
    :a; N; $!ba
    s/\(=[^,]*,\)\n/\1 /g
    s/\(&FCI\)\n/\1 /
    s/ORBSYM/\n&/g
    s/&END/ISYM=1,\n\//
    }' -i /tmp/header 
    
    # Then combine the new header with the rest of the giant data file,
    # using `ed` (see `man ed;info Ed`) and here-document
    ed "$1" <<EndOfEd
    1,${endheader}d
    :0r /tmp/header
    :wq
    EndOfEd
    
0
ответ дан 23 November 2019 в 07:58

Другие вопросы по тегам:

Похожие вопросы: