Я хочу заменить только первые k
экземпляры слова.
Как я могу это сделать?
Например. Скажем, файл foo.txt
содержит 100 вхождений слова «linux».
Мне нужно заменить только первые 50 случаев.
text-processing
sed
awk
нарендра-Чудхари
источник
источник
Ответы:
Первый раздел ниже описывает использование
sed
для изменения первых k вхождений в строке. Второй раздел расширяет этот подход, чтобы изменить только первые k-вхождений в файле, независимо от того, на какой строке они появляются.Линейно-ориентированное решение
В стандартном sed есть команда для замены k-го вхождения слова в строке. Если
k
3, например:Или можно заменить все вхождения на:
Ни то, ни другое вы не хотите.
GNU
sed
предлагает расширение, которое изменит k-й случай и все после этого. Если k равно 3, например:Их можно комбинировать, чтобы делать то, что вы хотите. Чтобы изменить первые 3 вхождения:
где
\n
полезно здесь , потому что мы можем быть уверены , что никогда не происходит на линии.Объяснение:
Мы используем три
sed
команды замещения:s/\<old\>/\n/g4
Это расширение GNU для замены четвертого и всех последующих вхождений
old
с\n
.Расширенная функция регулярного выражения
\<
используется, чтобы соответствовать началу слова и\>
совпадать с концом слова. Это гарантирует, что сопоставляются только полные слова. Расширенное регулярное выражение требует-E
опцииsed
.s/\<old\>/new/g
Только первые три вхождения
old
остаются, и это заменяет их всехnew
.s/\n/old/g
Четвертый и все остальные вхождения
old
были заменены\n
на первом шаге. Это возвращает их обратно в исходное состояние.Решение без GNU
Если GNU sed недоступен и вы хотите изменить первые 3 вхождения
old
наnew
, тогда используйте триs
команды:Это хорошо работает, когда
k
небольшое число, но плохо масштабируется до большогоk
.Так как некоторые не-GNU seds не поддерживают объединение команд с точкой с запятой, каждая команда здесь представлена со своей
-e
опцией. Также может быть необходимо убедиться, что выsed
поддерживаете символы границы слова,\<
и\>
.Файловое решение
Мы можем сказать sed прочитать весь файл и затем выполнить замены. Например, чтобы заменить первые три случая
old
использования sed в стиле BSD:Команды sed
H;1h;$!d;x
читают весь файл в.Поскольку вышеупомянутое не использует никакого расширения GNU, оно должно работать на sed BSD (OSX). Обратите внимание, думал, что этот подход требует,
sed
чтобы можно было обрабатывать длинные строки. GNUsed
должно быть хорошо. Те, кто использует не GNU-версию,sed
должны проверить ее способность обрабатывать длинные строки.С помощью GNU sed мы можем дополнительно использовать
g
описанный выше прием, но с\n
заменой на\x00
, чтобы заменить первые три вхождения:Этот подход хорошо масштабируется и
k
становится большим. Это предполагает, однако, что\x00
это не в вашей исходной строке. Поскольку невозможно поместить символ\x00
в строку bash, это обычно безопасное предположение.источник
tr '\n' '|' < input_file | sed …
. Но, конечно, это преобразует весь ввод в одну строку, и некоторые не-GNU seds не могут обрабатывать произвольно длинные строки. (2) Вы говорите: «… выше, строка в кавычках'|'
должна быть заменена любым символом или строкой символов…» Но вы не можете использоватьtr
для замены символа строкой (длиной> 1). (3) В последнем примере вы говорите-e 's/\<old\>/new/' -e 's/\<old\>/w/' | tr '\000' '\n'\>/new
. Кажется, это опечатка для-e 's/\<old\>/new/' -e 's/\<old\>/new/' -e 's/\<old\>/new/' | tr '\000' '\n'
.Использование Awk
Команды awk могут использоваться для замены первых N вхождений слова на замену.
Команды будут заменены, только если слово полностью соответствует.
В приведенных ниже примерах, я вместо первых
27
вхожденийold
сnew
Используя суб
Замена поля вручную
Выполнение проверки перед
ПОЛУЧЕННЫЕ РЕЗУЛЬТАТЫ
Например
в
источник
$i
немного, его отредактировали, спасибо :)Скажем, вы хотите заменить только первые три экземпляра строки ...
примечание: вышеупомянутое, скорее всего, не будет работать со встроенными комментариями
... или в моем примере с "1" ...
ВЫХОД:
Там я использую две известные техники. Во-первых, каждое вхождение
1
на линии заменяется на\n1
. Таким образом, поскольку я делаю рекурсивные замены далее, я могу быть уверен, что не заменим вхождение дважды, если моя строка замены содержит мою строку замены. Например, если я заменюhe
наhey
него, все равно будет работать.Я делаю это так:
Во-вторых, я рассчитываю замены, добавляя символ в
h
старое место для каждого вхождения. Как только я достигну трех, больше не произойдет. Если вы примените это к своим данным и измените\{3\}
общее количество замен, которые вы хотите, и/\n1/
адреса на то, что вы хотите заменить, вы должны заменить только столько, сколько пожелаете.Я сделал все
-e
для удобства чтения. POSIXly это может быть написано так:И ж / GNU
sed
:Помните также, что
sed
он ориентирован на строки - он не читает весь файл, а затем пытается повторить его, как это часто бывает в других редакторах.sed
это просто и эффективно. Тем не менее, часто удобно сделать что-то вроде следующего:Вот небольшая функция оболочки, которая объединяет ее в просто выполняемую команду:
Итак, с этим я могу сделать:
...и получить...
...или...
...получить...
... или, в соответствии с вашим примером (в меньшем порядке) :
источник
Краткая альтернатива в Perl:
Измените значение `$ n $ по своему вкусу.
Как это работает:
new
наold
(s/old/new/
) и всякий раз , когда это возможно, это увеличивает переменную$i
(++$i
).1 while ...
) до тех пор, пока он произвел меньше, чем$n
подстановок, и может сделать хотя бы одну замену в этой строке.источник
Используйте петлю оболочки и
ex
!Да, это немного глупо.
;)
Примечание. Может произойти сбой, если
old
в файле содержится менее 50 экземпляров . (Я не проверял это.) Если так, это оставило бы файл без изменений.Еще лучше использовать Vim.
Объяснение:
источник
Простое, но не очень быстрое решение - это циклическое переключение команд, описанных в /programming/148451/how-to-use-sed-to-replace-only-the-first-occurrence-in-a -файл
Эта конкретная команда sed, вероятно, работает только для GNU sed, и если newword не является частью oldword . Для не-GNU sed смотрите здесь, как заменить только первый шаблон в файле.
источник
С GNU
awk
вы можете установить разделительRS
на слово , чтобы быть замененными разделителей по границам слов. Тогда это случай установки разделителя записей на выходе для слова замены для первыхk
записей, в то же время сохраняя оригинальный разделитель записей для остаткаИЛИ
источник