У меня есть сценарий оболочки, который является в сущности 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 и перенаправление вывода в другой файл дают пустой файл.
head
и tail
создать главный файл и файл данных.Можно использовать cat
связать измененный главный файл и файл данных.
Эффективный способ распечатать строки из крупного файла с помощью awk, 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
sed
вероятно, НЕ лучший инструмент для этого, заняться расследованиями perl
. Однако Вы могли вновь заявить о проблеме как:
Извлеките Старый Заголовок из гигантского файла данных в собственный файл.
Скорректируйте извлеченный Старый Заголовок, для создания этого Новым Заголовком.
Замените Старый Заголовок Новым Заголовком в гигантском файле данных.
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