LINUX.ORG.RU

Как быстро заменить строку в миллионе файлов .php?

 ,


0

1

Всем привет,

Нужно решение - заменить строчку кода в миллионе файлов .php (строчка во всех файлах одинаковая естественно).

Т.е. есть 1кк+ файлов .php которые содержат небольшую строку, которую нужно заменить везде.

Решений много, но замена идет очень долго. Кто-то может посоветовать самое быстрое решение? Спасибо.

самое быстрое решение

только если замена такойже длины: упаковать в tar, натравить sed, распаковать. если не стремно то можно прямо на диск натравить (но sed помоему не умеет так), можно снять образ диска и натравить на него - заодно будет бэкап

бекапы рекомендуются!

Deleted
()

есть 1кк+ файлов .php

Как и нахера это получилось?

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

упаковка в tar и распаковка обратно сожрет больше времени чем работа скрипта на баше (к примеру). для ускорения можно запустить в несколько потоков. но работа с кучей отдельных файликов один х сожрет кучу времени.

Еще можно замудрить такой вот хак: если строки идентичной длины и уникальны, то можно намонстрячить програмку/скриптик который поменяет одну строку на другую прямо в соответствующем /dev/sda*. будет очень быстро, но косячнее.

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

который поменяет одну строку на другую прямо в соответствующем /dev/sda*. будет очень быстро

Сомневаюсь, что это будет быстро. 1кк файлов по 10Кб это всего лишь ~10Гб. Размер диска на порядок больше (скорее всего).

no-such-file ★★★★★
()
Ответ на: комментарий от pfg

сожрет больше времени чем работа скрипта на баше (к примеру).

скрипт на баше на каждый файлик будет запускать sed, как минимум, это и беспокоит автора

в принципе накидать на какомнить перле тузлу для inplace замены было бы быстрее всего

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

скрипт на баше на каждый файлик будет запускать sed

Не на каждый, а на группу файлов - сколько помещается в строку аргументов.

no-such-file ★★★★★
()
Ответ на: комментарий от Deleted

исключается мильон операция по открытию и закрытию файла. я думаю всем наглядно что скопировать один 10гб файл будет гораздо быстрее чем 10 000 файлов по 1мб к примеру.

sed закешируется в памяти и для его запуска в работу не будет операций с диском.

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

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

sed еще костыльный inplace делает - через временный файл, так шо тузла будет всяко эффективней

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

что скопировать один 10гб файл

Вот только проблемка, они разбросаны по диску в 1000Гб.

no-such-file ★★★★★
()
Ответ на: комментарий от Deleted

Это всё равно гораздо быстрее чем прочитать+записать+ещё раз записать как в твоём варианте. У тебя генерируется лишнее дикое количестов iops.

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

у меня 2*N+4, с скриптом 4*N (запустить sed, прочитать, записать в tmp, переименовать - это с учетом -i)

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

Можете, пожалуйста, привести пример команды, как именно полный синтаксис выглядит? спасибо

Merca709
() автор топика
Ответ на: комментарий от no-such-file

Мы пробовали юзать такую команду: time find ./ -type f -exec sed -i 's/TEXT-1/TEXT-2/g' {} \; Уже неделю идет замена на 1кк, пока еще не завершилась... Может сможете подсказать более лучший синтаксис, спасибо

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

Мы пробовали юзать такую команду: time find ./ -type f -exec sed -i 's/TEXT-1/TEXT-2/g' {} \

У вас реально на каждый файл отдельный sed и в один поток. Да ещё ждёт пока предыдущий sed отработает.

Написал же - Как быстро заменить строку в миллионе файлов .php? (комментарий) Запускает 1 sed на ~ 100 файлов и в 8 потоков параллельно.

no-such-file ★★★★★
()

Объединить все файлы php в файловой системе в один, натравить на него поиск с заменой, восстановить файловую систему )

vaddd ★☆
()

Если строка замены достаточно уникальная - вынести все php файлы в отдельную часть диска, пройтись с заменой по поверхности этой части диска )

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

Уже неделю идет замена на 1кк

А замена точно идёт? Что то слишком долго всё равно...

И

time find ./ -type f -exec sed -i 's/TEXT-1/TEXT-2/g' {} \;                                                                    
sed: невозможно прочитать : Нет такого файла или каталога

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Да, замена идет, но медленно. Эту команду запускаем из папки где лежат эти 1кк файлов.

Сорри, туплю, я не очень в Линуксе, а мой партнер-кодер отошел, вот эта команда, что вы привели:

find -type f -name '*.php' -print0 | xargs -0 -P 8 sed -i — 's/foo/bar/g'

Где тут вставлять текст_исходный и текст_который_надо_заменить?

Merca709
() автор топика
Ответ на: комментарий от no-such-file

Ваше решение выглядит пока наилучшим, если заработает, т.к. всякие шаманства с объединением файлов/упаковкой = сложны.

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

Почему sed, а не ed

Кстати да, забыл про него. Но он не везде есть, и вроде бы не может батч-обработку нескольких файлов = запуск на каждый файл. Не факт что выйдет быстрее.

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 2)
Ответ на: комментарий от no-such-file

А как будет полный синтаксис команды замены в итоге с помощью sed и ed?

Merca709
() автор топика
Ответ на: комментарий от no-such-file

вроде бы не может батч-обработку нескольких файлов = запуск на каждый файл

Я ed не умею. Вроде там похоже на ex режим в vim, в мануале по крайней мере описана команда r file, так что наверное всё можно сделать в одном процессе ed, скармливая ему команды в stdin. Готовый вариант не дам, простите меня ¡_¡

d_a ★★★★★
()
Ответ на: комментарий от no-such-file

К сожалению, команда приведенная выше (find -type f -name '*.php' -print0 | xargs -0 -P 8 sed -i — 's/TEXT-1/TEXT-2/g') не работает, проходит какое-то время, затем ошибки:

sed: -e expression #1, char 1: unknown command: `�' sed: -e expression #1, char 1: unknown command: `�' sed: -e expression #1, char 1: unknown command: `�'

и т.д.

При этом изменений на вскидку не видно.

Есть какие-то идеи, где ошибка?

Merca709
() автор топика
Ответ на: комментарий от no-such-file

При работе с диском (если это один физический диск, а не массив дисков) распараллеливание не только не даст выигрыша, но наоборот всё замедлит. Ведь физически к диску в каждый момент времени всё равно будет обращаться только один поток/процесс. И пока он блокирует диск, другие потоки/процессы до этого диска не доберутся. Зато вместо того, чтоб последовательно читать и писать данные, разные процессы будут поочерёдно обращаться к разным порциям этих данных, перемещая головки чтения/записи туда-сюда, что затратно по времени и быстрее изнашивает диск. Ну и само распараллеливание тоже потребует дополнительных ресурсов.

aureliano15 ★★
()
Ответ на: комментарий от Merca709
find -type f -name '*.php' -print0 | xargs -0 -P 8 -I {} sed -i 's/foo/bar/g' {}

Но я бы не стал в данном случае распараллеливать по причинам, изложенным в другом моём каменте в этой теме.

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

find -type f -name '*.php' -print0 | xargs -0 -P 8 sed -i — 's/foo/bar/g'

Где тут вставлять текст_исходный и текст_который_надо_заменить?

В команде sed исходный текст - foo, заменить на bar. Флаг g означает, что если в одной строке несколько таких подстрок, то заменить все (иначе заменится только первая).

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

Спасибо, уже разобрался, но выдает ошибку спустя время, не работает почему-то

Merca709
() автор топика
Ответ на: комментарий от aureliano15
find -type f -name '*.php' -print0 | xargs -0 -P 8 -I {} sed -i 's/foo/bar/g' {}

а как команда должна выглядеть без распараллеливания?

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

Это что за хрень? Неужели так трудно было скопипастить готовую команду?

find -type f -name '*.php' -print0 | xargs -0 -P 8 sed -i -- 's/TEXT-1/TEXT-2/g'
Ты где тут {} нашёл? Без распараллеливания 8 замени на 1 (поток).

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от aureliano15

распараллеливание не только не даст выигрыша, но наоборот всё замедлит

Чо правда? Sed не всё время читает/пишет. А кроме того, другие программы тоже хотят работать, больше активных sed-ов повысят % использования диска данной задачей. Просто ведь проверить, запустить на небольшом объёме в 1 поток и в 8.

Ведь физически к диску в каждый момент времени всё равно будет обращаться только один поток/процесс

Для этого придумали планировщики ввода-вывода.

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от Merca709

Видимо в TEXT-1 или TEXT-2 есть какие-то символы, которые ломают выражение.

no-such-file ★★★★★
()
Ответ на: комментарий от Merca709

find -type f -name '*.php' -print0 | xargs -0 -P 8 -I {} sed -i 's/foo/bar/g' {}

а как команда должна выглядеть без распараллеливания?

find -type f -name '*.php' -exec sed -i 's/foo/bar/g' {} \;

В данном варианте sed вызывается для каждого найденного файла. «{}» заменяется именем файла. Аргументы -exec заканчиваются «\;»

Можно ещё так:

sed -i 's/foo/bar/g' $(find -type f -name '*.php')

Этот вариант сначала найдёт все файлы, а потом вызовет единственный экземпляр sed для их последовательной обработки, что может сэкономить время. Но если файлов порядка миллиона, то вызов может завершиться ошибкой из-за слишком длинной командной строки (ведь вместо find ... командная оболочка попытается подставить весь миллион файлов за раз).

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

В данном варианте sed вызывается для каждого найденного файла

И дико тормозит.

Этот вариант сначала найдёт все файлы

А потом скажет, что command line too long.

Зачем что-то советовать, если ты не в теме? Кстати, что касается параллельной работы - тут затык не в скорости обмена с диском, а в том что много мелких файлов и тратится время на поиск. Нужно много времени, чтобы просто сделать find. В моём варианте find идёт независимо и параллельно от обработки и список файлов сохраняется в пайпе, откуда его потом раскидывает на sed-ы. Эти sed-ы уже не тормозят при чтении-записи, потому что нужные структуры файловой системы уже осели в кэше.

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от no-such-file

find -type f -name '*.php' -print0 | xargs -0 -P 8 sed -i — 's/TEXT-1/TEXT-2/g'

Ты где тут {} нашёл? Без распараллеливания 8 замени на 1 (поток).

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

Видимо действительно в самом рег. выр. какие-то недопустимые неэкранируемые символы присутствуют. Но чтоб это узнать, топикстартер должен выложить своё регулярное выражение.

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

какие-то недопустимые неэкранируемые символы присутствуют

Вангую, что используемый sed не умеет в utf-8. Судя по кракозябле в ошибке.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Ну я ведь то же самое написал с самого начала. :-)

Идеальный вариант - это всё затарить, как предлагали выше, но он сработает только если длина foo и bar абсолютно одинакова.

Можно ещё склеить файлы, сохранив внутри склеенного файла имена оригинальных, типа (for i in *.php; do echo «$i :»; cat *.php; done) > megafile, а потом обрабатывать этот мегафайл, после чего снова разделить его. Но это ещё сложнее.

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

Во-первых, всем спасибо за ответы! Продолжаю тестировать предложенные способы, как будут новости - сразу отпишу. Возможно я упустил один знак в

--
.

По поводу самой строки, которую меняем - она не содержит пробелов и каких-либо спец. символов, только набор латинских букв и цифр, пример:

firsttext-0205 надо заменить на новый текст second-text-35070

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

Странно,

find -type f -name '*.php' -print0 | xargs -0 -P 8 sed -i -- 's/firsttext-0205/second-text-35070/g'
УМВР. Ищите в общем, где-то в команде кривые символы должны быть. Может быть они не отображаются, но попадают при копипасте.

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 2)
Ответ на: комментарий от no-such-file

Все php-файлы (1кк) имеют кодировку UTF-8

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

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

no-such-file ★★★★★
()

миллион файлов .php ? Это ж просто безобразие какое-то ...

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