Подтвердить что ты не робот

Как найти/заменить и увеличить количество совпадений с помощью sed/awk?

Прямо к делу, мне интересно, как использовать grep/find/sed/awk для соответствия определенной строке (которая заканчивается номером) и увеличивать это число на 1. Ближайший я пришел, чтобы объединить a 1 до конца (который работает достаточно хорошо), потому что основной задачей является простое изменение значения. Вот что я сейчас делаю:

find . -type f | xargs sed -i 's/\(\?cache_version\=[0-9]\+\)/\11/g'

Так как я не мог понять, как увеличить число, я захватил все это и просто добавил "1". Раньше у меня было что-то вроде этого:

find . -type f | xargs sed -i 's/\?cache_version\=\([0-9]\+\)/?cache_version=\11/g'

Итак, по крайней мере, я понимаю, как захватить то, что мне нужно.

Вместо того, чтобы объяснять, для чего это, я просто объясню, что я хочу. Он должен найти текст в любом файле, рекурсивный, на основе текущего каталога (неважно, это может быть любой каталог, поэтому я бы его позже установил), который соответствует "? Cache_version =" с номером. Затем он увеличит это число и заменит его в файле.

В настоящее время материал, который у меня выше, работает, просто я не могу увеличивать число найденное в конце. Лучше было бы увеличить инкремент вместо добавления "1", чтобы будущие значения не были "11", "111", "1111", "11111" и т.д.

Я просмотрел десятки статей/объяснений, и достаточно часто предложение состоит в том, чтобы использовать awk, но я не могу для меня смешать их. Ближе всего я пришел к использованию awk, который на самом деле ничего не заменяет:

grep -Pro '(?<=\?cache_version=)[0-9]+' . | awk -F: '{ print "match is", $2+1 }'

Мне интересно, если есть какой-нибудь способ передать sed в конце и передать исходное имя файла, чтобы sed мог иметь имя файла и увеличивающийся номер (из awk) или что угодно что xargs имеет.

Технически это число не имеет значения; эта замена в основном заключается в том, чтобы убедиться, что там есть новый номер, 100% наверняка отличается от последнего. Так как я писал этот вопрос, я понял, что могу использовать системное время - секунды с эпохи (техника, часто используемая AJAX для устранения кэширования для последующих "идентичных" запросов). Я закончил с этим, и это кажется идеальным:

CXREPLACETIME=`date +%s`; find . -type f | xargs sed -i "s/\(\?cache_version\=\)[0-9]\+/\1$CXREPLACETIME/g"

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

Но я все равно хотел бы узнать исходный вопрос, увеличивая количество совпадающих чисел. Я предполагаю, что простым решением было бы сделать его bash script, но, тем не менее, я думал, что будет проще, чем рекурсивно перебирать каждый файл и проверять его содержимое для соответствия, а затем заменять, поскольку он просто увеличивая совпадающее число... не намного больше логики. Я просто не хочу писать ни в какие другие файлы или что-то в этом роде - он должен делать это на месте, например sed с опцией "i".

4b9b3361

Ответ 1

Я считаю, что поиск файла - не сложная часть для вас. Поэтому я просто перехожу к делу, чтобы выполнить расчет +1. Если у вас gnu sed, это можно сделать следующим образом:

sed -r 's/(.*)(\?cache_version=)([0-9]+)(.*)/echo "\1\2$((\3+1))\4"/ge' file

возьмем пример:

kent$  cat test 
ello
barbaz?cache_version=3fooooo
bye

kent$  sed -r 's/(.*)(\?cache_version=)([0-9]+)(.*)/echo "\1\2$((\3+1))\4"/ge' test     
ello                                                                             
barbaz?cache_version=4fooooo
bye

вы можете добавить опцию -i, если хотите.

изменить

/e позволяет передавать согласованную часть во внешнюю команду и подставлять результат выполнения. Только Gnu sed.

см. этот пример: используются внешние команды/инструменты echo, bc

kent$  echo "result:3*3"|sed -r 's/(result:)(.*)/echo \1$(echo "\2"\|bc)/ge'       

дает результат:

result:9

вы можете использовать другую мощную внешнюю команду, например cut, sed (снова), awk...

Ответ 2

Эта команда perl будет искать все файлы в текущем каталоге (без прохождения через нее, вам понадобится модуль File::Find или аналогичный для этой более сложной задачи) и увеличит число строк, которое соответствует cache_version=. Он использует флаг /e регулярного выражения, которое оценивает замещающую часть.

perl -i.bak -lpe 'BEGIN { sub inc { my ($num) = @_; ++$num } } s/(cache_version=)(\d+)/$1 . (inc($2))/eg' *

Я тестировал его с file в текущем каталоге со следующими данными:

hello
cache_version=3
bye

Он создает резервные копии исходного файла (ls -1):

file
file.bak

И file теперь с помощью:

hello
cache_version=4
bye

Я надеюсь, что это может быть полезно для того, что вы ищете.


UPDATE, чтобы использовать File::Find для перемещения каталогов. Он принимает * как аргумент, но отбрасывает их с найденными с помощью File::Find. Каталог для начала поиска - это текущий запуск script. Он жестко закодирован в строке find( \&wanted, "." ).

perl -MFile::Find -i.bak -lpe '

    BEGIN { 
        sub inc { 
            my ($num) = @_; 
            ++$num 
        }

        sub wanted {
            if ( -f && ! -l ) {  
                push @ARGV, $File::Find::name;
            }
        }

        @ARGV = ();
        find( \&wanted, "." );
    }

    s/(cache_version=)(\d+)/$1 . (inc($2))/eg

' *

Ответ 3

Версия Pure sed:

Эта версия не имеет зависимости от других команд или переменных среды. Он использует явный перенос. Для переноса я использую символ @, но другое имя можно использовать, если хотите. Используйте то, чего нет в вашем исходном файле. Сначала он находит SEARCHSTRING<number> и добавляет @к нему. Он повторяет увеличивающиеся цифры, у которых есть ожидающий перенос (т.е. После него есть символ переноса: [0-9]@) Если 9 был увеличен, этот приращение дает само перенос, и процесс будет повторяться до тех пор, пока не будет больше ожидающих переносов. Наконец, переносы, которые были получены, но не добавлены к цифре, но заменены на 1.

sed "s/SEARCHSTRING[0-9]*[0-9]/&@/g;:a {s/[email protected]/1/g;s/[email protected]/2/g;s/[email protected]/3/g;s/[email protected]/4/g;s/[email protected]/5/g;s/[email protected]/6/g;s/[email protected]/7/g;s/[email protected]/8/g;s/[email protected]/9/g;s/[email protected]/@0/g;t a};s/@/1/g" numbers.txt

Ответ 4

Это некрасиво (я немного ржавый), но здесь начинаем использовать sed:

orig="something1" ;
text=`echo $orig | sed "s/\([^0-9]*\)\([0-9]*\)/\1/"` ;
num=`echo $orig | sed "s/\([^0-9]*\)\([0-9]*\)/\2/"` ;
echo $text$(($num + 1))

С исходным именем файла ($orig) "something1" sed отделяет текст и числовые части от $text и $num, затем они объединяются в последнем разделе с добавленным числом, что приводит к something2.

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

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