Можно попробовать следующие Дистрибутивы Linux
Существуют намного больше, и у большинства из них есть поддержка IPv6. Необходимо будет проверить их индивидуально.
Также возражайте смотреть эта Страница Википедии.
В первом разделе описано использование sed
для изменения первых k-повторов на линии. Вторая секция расширяет этот подход, изменяя только первые k-повторжения в файле, независимо от того, на какой строке они появляются.
Со стандартным sed существует команда для замены k-го появления слова на строке. Если k
равно 3, например:
sed 's/old/new/3'
Или можно заменить все вхождения на:
sed 's/old/new/g'
Ни то, ни другое.
GNU sed
предлагает расширение, которое изменит k-е вхождение и все последующее. Если k равно 3, например:
sed 's/old/new/g3'
Они могут быть объединены для того, чтобы делать то, что вы хотите. Измените первые 3 вхождения:
$ echo old old old old old | sed -E 's/\<old\>/\n/g4; s/\<old\>/new/g; s/\n/old/g'
new new new old old
где \n
здесь полезно, потому что мы можем быть уверены, что это никогда не произойдет на строке.
Мы используем три команды замены sed
:
s/\
Это расширение GNU для замены четвертого и всех последующих вхождений old
на \n
.
Расширенная функция регекса \<
используется для совмещения начала слова и \>
для совмещения конца слова. Это обеспечивает соответствие только полным словам. Расширенный регекс требует, чтобы опция -E
использовалась для sed
.
s/\
Остались только первые три вхождения -E
старого, и это заменяет их все на -New
.
s/\n/old/g
На первом этапе четвертое и все остальные вхождения старого
были заменены на \n
. Это возвращает их в исходное состояние.
Если GNU sed недоступно и вы хотите изменить первые 3 вхождения старого
на новое
, то используйте три команды s
:
$ echo old old old old old | sed -E -e 's/\<old\>/new/' -e 's/\<old\>/new/' -e 's/\<old\>/new/'
new new new old old
Это хорошо работает, когда k
небольшое число, но плохо масштабируется до большого k
.
Поскольку некоторые не-GNU седаны не поддерживают объединения команд с точкой с запятой, каждая команда здесь вводится со своей собственной -e
опцией. Также может потребоваться проверить, что ваш sed
поддерживает символы границ слов, \<
и \>
.
Мы можем сказать sed прочитать весь файл и затем выполнить замены. Например, чтобы заменить первые три вхождения old
с помощью команды sed:
sed -E -e 'H;1h;$!d;x' -e 's/\<old\>/new/' -e 's/\<old\>/new/' -e 's/\<old\>/new/'
Команды sed H;1h;$!d;x
прочитать весь файл в формате BSD.
Поскольку вышеприведенное не использует никаких расширений GNU, оно должно работать на BSD (OSX) sed. Обратите внимание, подумайте, что такой подход требует sed
, который может работать с длинными строками. GNU sed
должно быть в порядке. Те, кто пользуется не GNU-версией sed
, должны проверить его способность работать с длинными линиями.
С помощью GNU sed мы можем далее использовать g
трюк, описанный выше, но с \n
, заменив его на \x00
, чтобы заменить первые три случая:
sed -E -e 'H;1h;$!d;x; s/\<old\>/\x00/g4; s/\<old\>/new/g; s/\x00/old/g'
Этот подход масштабируется так же хорошо, как k
становится большим. Однако это предполагает, что \x00
не находится в исходной строке. Так как невозможно поместить символ \x00
в бэш-строку, это обычно безопасное предположение.
Команды awk могут быть использованы для замены первых N вхождений слова на замену.
Команды будут заменяться только в том случае, если слово полностью совпадает.
В примерах ниже я заменяю первые 27
появления old
на new
Используя sub
awk '{for(i=1;i<=NF;i++){if(x<27&&$i=="old"){x++;sub("old","new",$i)}}}1' file
Эта команда проходит через каждое поле до тех пор, пока оно не совпадет с
old
, она проверяет, что счетчик находится ниже 27, приращения и заменяет первое совпадение на строке. Затем переходит к следующему полю/строке и повторяет.
Замена поля вручную
awk '{for(i=1;i<=NF;i++)if(x<27&&$i=="old"&&$i="new")x++}1' file
Аналогично команде до этого, но так как в нем уже есть маркер, на котором поле находится до
($i)
, он просто меняет значение поля сстарого
нановое
.
Проверка перед
awk '/old/&&x<27{for(i=1;i<=NF;i++)if(x<27&&$i=="old"&&$i="new")x++}1' file
Проверка того, что строка содержит старое и счетчик ниже 27
SHOULD
обеспечивает небольшой прирост скорости, так как он не будет обрабатывать строки, если они ложные.
RESULTS
E.g.
old bold old old old
old old nold old old
old old old gold old
old gold gold old old
old old old man old old
old old old old dog old
old old old old say old
old old old old blah old
to
new bold new new new
new new nold new new
new new new gold new
new gold gold new new
new new new man new new
new new new new dog new
new new old old say old
old old old old blah old
С помощью GNU awk
вы можете установить разделитель записей RS
на заменяемое слово , разделенное границами слов. Тогда это случай установки разделителя записей на выходе на слово замены для первых k
записей с сохранением исходного разделителя записей для остатка
awk -vRS='\\ylinux\\y' -vreplacement=unix -vlimit=50 \
'{printf "%s%s", $0, NR <= limit? replacement: RT}' file
ИЛИ
awk -vRS='\\ylinux\\y' -vreplacement=unix -vlimit=50 \
'{printf "%s%s", $0, limit--? replacement: RT}' file
Простое, но не очень быстрое решение - это перехват команд, описанных в https://stackoverflow.com/questions/148451/how-to-use-sed-to-replace-only-the-first-occurrence-in-a-file
for i in $(seq 50) ; do sed -i -e "0,/oldword/s//newword/" file.txt ; done
Эта конкретная команда sed, вероятно, работает только для GNU sed и если новое слово не является частью старого слова . Для не-GNU sed смотрите здесь как заменить только первый шаблон в файле.
Краткая альтернатива на Perl:
perl -pe 'BEGIN{$n=3} 1 while s/old/new/ && ++$i < $n' your_file
Измените значение `$ n $ по своему вкусу.
Как это работает:
новый
на старый
( s / old / new /
) и всякий раз, когда может, он увеличивает переменную $ i
( ++ $ i
). 1, пока ...
) до тех пор, пока он сделал менее $ n
замен в общей сложности и может произвести хотя бы одну замену на эта линия. Скажем, вы хотите заменить только первые три экземпляра строки...
seq 11 100 311 |
sed -e 's/1/\
&/g' \ #s/match string/\nmatch string/globally
-e :t \ #define label t
-e '/\n/{ x' \ #newlines must match - exchange hold and pattern spaces
-e '/.\{3\}/!{' \ #if not 3 characters in hold space do
-e 's/$/./' \ #add a new char to hold space
-e x \ #exchange hold/pattern spaces again
-e 's/\n1/2/' \ #replace first occurring '\n1' string w/ '2' string
-e 'b t' \ #branch back to label t
-e '};x' \ #end match function; exchange hold/pattern spaces
-e '};s/\n//g' #end match function; remove all newline characters
Примечание: вышеописанное, скорее всего, не будет работать со встроенными комментариями
... или, в моем примере, с "1"...
22
211
211
311
Там я использую две примечательные техники. Во-первых, каждое появление 1
на строке заменяется на \n1
. Таким образом, так как я делаю рекурсивные замены далее, я могу быть уверен, что не буду заменять вхождение дважды , если в строке моей замены будет содержаться моя строка замены. Например, если я заменю he
на hey
, это все равно сработает.
Я делаю это так:
s/1/\
&/g
Во-вторых, я считаю замены, добавляя символ в h
старую строку для каждого вхождения. Как только я достигаю трёх больше не происходит. Если вы примените это к своим данным и измените \{3\}
на желаемые вами общие замены, а /\n1/
адреса на все, что вы хотите заменить, вы должны заменять только столько, сколько захотите.
Я сделал все -e
только для удобочитаемости. POSIXly Можно было бы написать так:
nl='
'; sed "s/1/\\$nl&/g;:t${nl}/\n/{x;/.\{3\}/!{${nl}s/$/./;x;s/\n1/2/;bt$nl};x$nl};s/\n//g"
И w/ GNU sed
:
sed 's/1/\n&/g;:t;/\n/{x;/.\{3\}/!{s/$/./;x;s/\n1/2/;bt};x};s/\n//g'
Помните также, что sed
ориентирован на чтение строк - он читает не во всем файле, а затем пытается зациклиться на нем, как это часто бывает в других редакторах. sed
прост и эффективен. При этом часто удобно делать что-то вроде:
Вот небольшая функция оболочки, которая объединяет ее в просто выполняемую команду:
firstn() { sed "s/$2/\
&/g;:t
/\n/{x
/.\{$(($1))"',\}/!{
s/$/./; x; s/\n'"$2/$3"'/
b t
};x
};s/\n//g'; }
Так что с помощью этого я могу сделать:
seq 11 100 311 | firstn 7 1 5
... и получить....
55
555
255
311
... или...
seq 10 1 25 | firstn 6 '\(.\)\([1-5]\)' '\15\2'
... чтобы...
10
151
152
153
154
155
16
17
18
19
20
251
22
23
24
25
... или, в соответствии с вашим примером (на меньший порядок):
yes linux | head -n 10 | firstn 5 linux 'linux is an os kernel'
linux is an os kernel
linux is an os kernel
linux is an os kernel
linux is an os kernel
linux is an os kernel
linux
linux
linux
linux
linux
Используйте цикл оболочки и ex
!
{ for i in {1..50}; do printf %s\\n '0/old/s//new/'; done; echo x;} | ex file.txt
Да, это немного глупо.
;)
Примечание. Это может привести к сбою, если в файле менее 50 экземпляров старого
. (Я не тестировал его.) Если это так, он оставит файл без изменений.
А еще лучше использовать Vim.
vim file.txt
qqgg/old<CR>:s/old/new/<CR>q49@q
:x
Объяснение:
q # Start recording macro
q # Into register q
gg # Go to start of file
/old<CR> # Go to first instance of 'old'
:s/old/new/<CR> # Change it to 'new'
q # Stop recording
49@q # Replay macro 49 times
:x # Save and exit