Использование awk для внесения изменений в n-й символ в n-й строке файла

#linux #awk

Вопрос:

Я написал команду awk

awk 'NR==5 {sub(substr($1,14,1),(substr($1,14,1) 1)); print "test.py"}' > test.py

Это попытка изменить 14-й символ в 5-й строке файла python. По какой-то причине это не прекращает выполнение, и я должен его прервать. Это также удаляет содержимое файла.

Пример ввода:

 import tools

tools.setup(
    name='test',
    tagvisc='0.0.8',
    packages=tools.ges(),
    line xyz
)
 

`

Вывод:

 import tools

tools.setup(
    name='test',
    tagvisc='0.0.9',
    packages=tools.ges(),
    line xyz
)
 

Комментарии:

1. Что вы пытаетесь сделать? Предоставьте пример ввода с ожидаемым результатом.

2. @anubhava добавил это

3. существует также скрытая проблема с sub , предположим, у вас есть значение 3.4.3 . Это увеличит первое вхождение 3.

4. Вам нужно будет использовать split() , чтобы разделить это поле на массив, а затем увеличить и собрать его обратно. См. Руководство пользователя GNU Awk — Функции обработки строк

5. Я бы выбрал Perl, здесь, perl -i -pe 's/^h*tagvisc='"'"'d .d .Kd /($amp; 1)/e' test.py , или, если строка важна, perl -i -pe 's/^h*tagvisc='"'"'d .d .Kd /($amp; 1)/e if $. == 5' test.py

Ответ №1:

Если я понимаю нюансы того, что вам нужно сделать сейчас, вам нужно будет разделить первое поле 5-й записи на массив, используя "." в качестве fieldsep, а затем удалить ""," из конца 3-го элемента массива (необязательно), прежде чем увеличивать число и собирать поле обратно. Вы можете сделать это с помощью:

 awk '{split($1,a,"."); sub(/["],/,"",a[3]); $1=a[1]"."a[2]"."(a[3] 1)"","}1'
 

( NR==5 например, опущено)

Пример использования / вывода

 $ echo 'tagvisc="3.4.30"', | 
  awk '{split($1,a,"."); sub(/["],/,"",a[3]); $1=a[1]"."a[2]"."(a[3] 1)"","}1'
tagvisc="3.4.31",
 

Я оставлю перенаправление на временный файл, а затем вернусь к оригиналу. Дайте мне знать, если это не то, что вам нужно.

Добавив NR == 5 , что у вас будет

 awk 'NR==5 {split($1,a,"."); sub(/["],/,"",a[3]); $1=a[1]"."a[2]"."(a[3] 1)"","}1' test.py > tmp; mv -f tmp test.py
 

Комментарии:

1. он добавляется ..1 к каждой строке. Я думаю, нам придется указать номер строки

2. Вам нужно добавить NR==5 условие — я оставил его для примера. Также обратите внимание, что я исправил опечатку "," "." между a[2] и a[3]

3. На самом деле вы можете использовать '{split($1,a,"."); $1=a[1]"."a[2]"."(a[3] 1)"","}1' as awk , чтобы отбросить конечные нецифровые значения при преобразовании в числовое значение при добавлении 1 .

4. Хорошо, это работает, но в файле есть несколько пробелов перед строкой, из-за чего они исчезают.

5. Да, есть несколько способов справиться с этим, ни один из них не является тривиальным. Причина в том, что вы используете значение по умолчанию FS , которое разделяет поля на пробелы. Множественные пробелы обрабатываются как один разделитель. Вы можете сохранить количество начальных пробелов $0 и добавить их обратно, если это требуется.

Ответ №2:

Отойдите от фиксированного номера строки ( NR==5 ) и фиксированной позиции символа ( 14 ) и вместо этого посмотрите на динамический поиск того, что вы хотите изменить / увеличить, например:

 $ cat test.py
import tools

tools.setup(
    name='test',
    tagvisc='0.0.10',
    packages=tools.ges(),
    line xyz
)
 

Одна awk идея увеличить 10 (3-я строка, 3-я числовая строка в строке):

 awk '
/tagvisc=/ { split($0,arr,".")                             # split line on periods
             sub("." arr[3] 0 "47","." arr[3] 1 "47")  # replace .<oldvalue>47 with .<newvalue>47; 47 == single quote
           }
1
' test.py
 

Примечания:

  • arr[3] = 10', ; with arr[3] 0 awk возьмет самое левое полностью числовое содержимое, удалит все остальное, затем добавит 0 , оставив нам arr[3] = 10 ; та же логика применяется для arr[3] 1 ( arr[3] 1 = 11 ); в основном трюк для отбрасывания любого суффикса, который не является числовым
  • если в файле есть несколько строк со строкой tagvisc='x.y.z' , это изменится z во всех строках; мы можем обойти это, добавив еще немного логики, чтобы изменить только first вхождение, но я пока оставлю это, предполагая, что это не проблема

Это генерирует:

 import tools

tools.setup(
    name='test',
    tagvisc='0.0.11',
    packages=tools.ges(),
    line xyz
)
 

Если цель состоит в том, чтобы перезаписать исходный файл новыми значениями, у вас есть пара вариантов:

 # use temporary file:

awk '...' test.py > tmp ; mv tmp test.py

# if using GNU awk, and once accuracy of script has been verified:

awk -i inplace '...' test.py
 

Комментарии:

1. Я отлаживаю, но это не вносит никаких изменений в мой файл.

2. вы используете временный файл или awk -i inplace ?

3. Да, с использованием файла tmp

4. пожалуйста, обновите вопрос первыми 7 строками вашего файла

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

Ответ №3:

Использование awk для внесения изменений в n-й символ в [m-й] строке файла:

 $ awk 'BEGIN{FS=OFS=""}NR==5{$18=9}1' file # > tmp amp;amp; mv tmp file
 

Выводит:

 import tools

tools.setup(
    name='test',
    tagvisc='0.0.9',   <----- this is not output but points to what changed
    packages=tools.ges(),
    line xyz
)
 

Объяснено:

 $ awk '
BEGIN {
    FS=OFS=""    # set the field separators to empty and you can reference
}                # each char in record by a number
NR==5 {          # 5th record
    $18=9        # and 18th char is replaced with a 9
}1' file         # > tmp amp;amp; mv tmp file # output to a tmp file and replace
 

Обратите внимание: некоторые awk (возможно, все, кроме GNU awk) завершатся ошибкой, если вы попытаетесь заменить многобайтовый символ на однобайтовый (например, utf8 ä (0xc3 0xa4) на a (0x61), что приведет к 0x61 0xa4). Естественно ä , перед позицией, которую вы хотите заменить, ваши вычисления будут отклонены на 1.

О да, вы можете заменить один символ несколькими символами, но не наоборот.

Ответ №4:

что-то вроде этого…

 $ awk 'function join(a,k,s,sep) {for(k in a) {s=s sep a[k]; sep="."}  return s}
       BEGIN {FS=OFS="""}
       /^tagvisc=/{v[split($2,v,".")]  ; $2=join(v)}1' file > newfile
 

Ответ №5:

Использование GNU awk для 3-го аргумента для сопоставления () и редактирования «на месте»:

 $ awk -i inplace '
    match($0,/^([[:space:]]*tagvisc=47)([^47] )(.*)/,a) {
        split(a[2],ver,".")
        $0 = a[1] ver[1] "." ver[2] "." ver[3] 1 a[3]
    }
    { print }
' test.py
 
 $ cat test.py
import tools

tools.setup(
    name='test',
    tagvisc='0.0.9',
    packages=tools.ges(),
    line xyz
)