LINUX.ORG.RU

sed и замена символов в большом файле

 , , ,


1

5

Здравствуйте Столкнулся с проблемой что sed не отрабатывает на файле размером 15 гигабайт, на части этого файла отрабатывает. Не отрабатывает это значит что-то делает, ошибок нет, но файл не меняется.

задача была такая 1. заменить \n на ',' «концов строки» может быть несколько \n\n\n а запятая нужна одна, в самом конце запятая не нужна 2. в начале файла воткнуть 1 символ 3. в конец файла воткнуть 1 символ

в итоге файл с 111111 222222 333333

нужно было переделать в [11111,22222,33333] не меняя имени файла

нашел такие варианты(возможно не самые лучшие) sed -i -r ':a;N;$!ba;s/\n+/,/g' FILE; sed -i -e '1 s/^/[/;' FILE; echo «]» >> FILE

попробовал 1000 строк и 3 гигабайта и уже на этом объеме проблема, попробую найти размер с которого начинается проблема

ЕМНИП, sed с ключем -i делает копию файла в /tmp, соотвественно, если /tmp у тебя смонтирован в память, то большой файл туда просто не влезает.

Deleted
()
Последнее исправление: MyLittleLoli (всего исправлений: 1)

Насколько я помню (возможно ошибаюсь), единственный способ заставить sed обрабатывать файлы не построчно (чтобы заменять \n) приводит к тому, что весь файл загружается в память. Так что для больших файлов твой подход может не работать.

Попробуй лучше вот так:

tr "\n" "," <INPUT | sed -re 's/^/[/;s/,+/,/g;s/,+$/]/' >OUTPUT

Deleted
()
Последнее исправление: Deleted (всего исправлений: 3)
Ответ на: комментарий от Deleted

нет не в память, места много, похоже граница проблем проходит по отметке 2Gb

даже если смотреть глазами и рефрешить папку видно ,что создается бакап файл, он увеличивается в размерах, добегает до конца и исчезает.

Sintetik
() автор топика

sed -i

он создает временный файл в /tmp, потом перезаписывает, сделай явно sed ... > /раздел-заведомо-с-достаточным-местом/файл

anonymous
()
Ответ на: комментарий от Deleted

Не, так всё равно выжирает память. Не могу придумать как при помощи sed обрабатывать файлы по несколько гигабайт на строку. Видимо проще будет решить твою задачу небольшой программой на C или любом другом нормальном ЯП.

Deleted
()
Ответ на: комментарий от Sintetik

похоже граница проблем проходит по отметке 2Gb

ну может еще в файле слишком длинная строка в этом месте, можно попробовать проверить: awk '(b=length($0)) > a {a=b} END {print a}'

anonymous
()

15 гигабайт ... [11111,22222,33333]

а кто его потом будет парсить-то?

без sed тогда уж: { printf '['; <infile ruby -pe 'chomp! if $<.eof?' | tr -s '\n' ,; printf ']'; } >outfile (вместо ruby можно awk, но многословнее)

anonymous
()
Ответ на: комментарий от anonymous

> ruby -pe 'chomp! if $<.eof?'

а блин, тоже навернется, проще через dd скипнуть последний \n

anonymous
()
Ответ на: комментарий от anonymous

есть специальный инструмент, просто файл приходит к нам слегка кривой, нужно слегка его подправить и тогда его можно будет скормить парсеру.

ruby на машине нет, нужно использовать стандартные утилиты из поставки линукса

Sintetik
() автор топика
Ответ на: комментарий от Sintetik

похоже вариант tr «\n» "," < in.json >> out.json проходит но tr «\n» "," < in.json >> in.json входит в какой-то бесконечный цикл и файл разрастается, приходится рубить команду

как сделать чтобы без создания новых файлов?

Sintetik
() автор топика
Ответ на: комментарий от Sintetik

как сделать чтобы без создания новых файлов?

в начале файла воткнуть 1 символ

простыми утилитами вряд ли получится, а почему перезаписать out.json -> in.json не вариант?

anonymous
()
Ответ на: комментарий от anonymous

не то чтобы не вариант, просто неудобно, файлов много и они обрабатываются в цикле по маске, маска имени содержит значимую информацию и терять ее нельзя. т.е. можно переписывать, но нужно в итоге вернуться к изначальному «name.ext» а промежуточные файлы е должны накапливаться

Sintetik
() автор топика
Ответ на: комментарий от Sintetik

надо отделить проблему с именем файла, от сути, того как обработать этот файл.

Например, ты в цикле

for fn in *; do
    myscript "${fn}" > "${fn}.modified" && mv -f "${fn}.modified" "${fn}"
done

пробегаешь по файлам в текущем каталоге, и у тебя имя никуда не пропадает. Это позволит кстати не испортить исходный файл при неудачной модификации, и не затрет его.

А вот над myscript надо поработать. Я бы на питоне что-нибудь сделал, думаю, покурив документацию. Раз столкнулся с проблемами в sed на 2GB, значит в стандартных тулзах еще на 31-битное ограничение наткнешься, только нервы потратишь.

Deleted
()
Последнее исправление: Deleted (всего исправлений: 3)
Ответ на: комментарий от Sintetik

нужно использовать стандартные утилиты из поставки линукса

а, это не заметил. Ну, питон довольно стандартен, тем более если это rhel то там он точно уже стоит, правда 2й версии (3й в репах есть). Я бы не стал связываться с программой на С, еслм у тебя нет опыта в этом. Лучше покурить питон и тут тебе советов дадут по улучшению скрипта

Deleted
()
Ответ на: комментарий от Deleted

вариант tr -s «\n» "," < in.json >> out.json отработал нормально на 15Gb так что не вижу пока необходимости в писании программ добавил -s чтобы несколько \n схлопывать

осталась проблемка - последнюю \n в конце файла он тоже заменяет на "," ,а это не нужно, пока вылез из положения 1. сначала добавляя [ и ] потом меняю \n и последняя запятая остается за скобкой ], это не красиво, но работает

пока так

sed -i -r '1 s/^/[/;' in.json; echo «]» >> in.json; tr -s «\n» "," < in.json >> out.json; mv out.json in.json;

вот как бы обойтись без переименования?

Sintetik
() автор топика
Ответ на: комментарий от Sintetik

одна строка это десятки MGB а вот сам файл десятки Gb это сервер, на нем 32Gb памяти

Важно то, что у тебя на выходе будет одна гигантская строка на десятки гигабайт.

Deleted
()
Ответ на: комментарий от Deleted

вариант выше работает, надо только его дорихтовать до красивого sed -i -r '1 s/^/[/;' in.json; echo «]» >> in.json; paste -s -d in.json > out.json; mv out.json in.json;

так решается проблема последней запятой

Sintetik
() автор топика

sed - Stream EDitor.
Use ex luke.

mos ★★☆☆☆
()

Можно на flex-e

%%
[\n]+ {printf(",");}
%%
int yywrap(){return 1;}
int main(){while(yylex());return 0;}
/*
flex this_file.l
gcc -o xxx lex.yy.c
./xxx <in_file >out_file
*/
Это про переводы строки на запятую.

anonymous
()
Ответ на: комментарий от anonymous

ему же надо ...]

tr тоже скобки не ставит, я про него говорил.

да и что насчет "-s"?

А вот это я протупил, факт, на пустых строках будет ,,,,

massimus ★★★
()
Ответ на: комментарий от massimus

в наших файлах нет пустых строк, поэтому paste работает, проверил

1111

2222 3333

действительно становится 1111,,,2222,3333

пока есть рабочее решение, но нужно правильное

Sintetik
() автор топика
Ответ на: комментарий от Sintetik

но нужно правильное

Не менять оригинал, держи

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

int main(int argc, char const *argv[])
{
	
	FILE *pinputf;
	FILE *poutputf;

	if(argc < 2)
	{
		printf("use: %s  input_file ouput_file\n",argv[0]);
		exit(0);
	};

	pinputf   = fopen(argv[1],"r");
	poutputf  = fopen(argv[2],"w");

	assert(pinputf);
	assert(poutputf);

	fprintf(poutputf,"%c", '[' );

	int ch;
	bool separator = false;
	while((ch = fgetc(pinputf)) != EOF)
	{
		if(ch == '\n')
		{
			if(separator == false)
			{
		 		ch = ',';
		 		separator = true;
		 	}else{

		 		continue;
		 	}
		}else{
			separator = false;
		};
		fprintf(poutputf, "%c",ch);
	}

	fseek(poutputf,-1,SEEK_END);
	fprintf(poutputf,"%c",']');

	fclose(pinputf);
	fclose(poutputf);

	return 0;
}

dron@gnu:~$ gcc test.c -o replace ; ./replace big_txt_file ./out_big_txt_file
dron@gnu:~$ cat big_txt_file 
0


111

11




dron@gnu:~$ cat ./out_big_txt_file 
[0,111,11]dron@gnu:~$ 

Dron ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.