LINUX.ORG.RU

Замена значения в файле


0

1

Доброго времени суток.

Имеется файл вида

ID1  Name1   Param1   Num11  Num12  Num13 ... Num1N  Blabla1^M
...
IDK  NameK   ParamK   NumK1  NumK2  NumK3 ... NumKN  Blabla1^M

где NumIJ - некоторое число. Нужно в строке с определенным ID заменить это число на другое. Про число известна только его позиция в строке (например, 7 столбец). При этом между столбцами может быть произвольное число whitespace-символов. Написал что-то вроде

awk -v val=$VALUE '{if ($1=="IDX") sub(".*", val, $7); print > "./tmp";}' FILENAME
Возникло две проблемы. Во-первых, естественно, теряется форматирование, все пробельные символы после сборки строки обратно заменяются одним. Это неприятно, но не критично. Во-вторых, теряется CRLF, что уже плохо (на выходе нужен файл с переводом строк в формате CRLF).

Вопрос: как быть? Что-то ничего хорошего в голову пока не приходит.

Спасибо.



Последнее исправление: alexander1389 (всего исправлений: 1)
$ cat test
ID1  Name1   Param1   Num11  Num12  Num13 ... Num1N  Blabla1
IDX    val     val3     va4   val5  ssss6 ... 88888  BlaBla1
IDK  NameK   ParamK   NumK1  NumK2  NumK3 ... NumKN  Blabla1
$ file test
test: ASCII text, with CRLF line terminators
$ PERLIO=:unix:crlf perl -lapse'if($F[0] eq $search){@_=split/(\s+)/,$_; $_[2*($index-1)]=$replace; $_=join"",@_}' -- -index=8 -search=IDX -replace=VALUE test
ID1  Name1   Param1   Num11  Num12  Num13 ... Num1N  Blabla1
IDX    val     val3     va4   val5  ssss6 ... VALUE  BlaBla1
IDK  NameK   ParamK   NumK1  NumK2  NumK3 ... NumKN  Blabla1
$ PERLIO=:unix:crlf perl -lapse'if($F[0] eq $search){@_=split/(\s+)/,$_; $_[2*($index-1)]=$replace; $_=join"",@_}' -- -index=8 -search=IDX -replace=VALUE test | file -
/dev/stdin: ASCII text, with CRLF line terminators

Бонус: можно добавить параметр -i.bak (до --) и получить редактирование файла на месте.

AITap ★★★★★
()
Последнее исправление: AITap (всего исправлений: 2)
id=1
column=7
value=100

printf 'ID1        2\t3 4 5 6 7            8 9 10\r\n' \
	| sed -r "s/^(ID${id}(\s+\S+){$(expr "${column}" - 2)}\s+)(\S+)(.*)$/\1${value}\4/" \
	| cat -v

=> ID1 2 3 4 5 6 100 8 9 10^M

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

Забыл сказать важную вещь: система встраиваемая, интерпретатора перл там нет, да и вряд ли появится. Поэтому решение не подходит, нужно что-то grep-sed-awk-шное, а точнее даже busybox-овское. Но все равно спасибо!

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

Завтра попробую, визуально кажется, что должно сработать.. Спасибо!

alexander1389
() автор топика
#!/bin/bash

function data() {
cat << EOF
ID1  Name1   Param1   Num11  Num12  Num13 ... Num1N  Blabla1
IDX    val     val3     va4   val5  ssss6 ... 88888  BlaBla1
IDK  NameK   ParamK   NumK1  NumK2  NumK3 ... NumKN  Blabla1
EOF
}

VALUE=
data | awk -v val=$VALUE -v N=7 '
$1=="IDX" {
  In=$0; Out=""; n=0;
  while(match(In,/[^ ]+ */,M)) {
    In=substr(In,length(M[0])+1)
    if(++n==N) sub($N,val,M[0])
    Out=Out""M[0]
  }
  $0=Out
} 1'
anonymous
()

естественно, теряется форматирование

Ну а как хотел? Если, допустим, новое значение превышает ширнину поля - придется расширять, тогда если меньше, придется проверять - не нужно ли сжимать. Для crlf:

BEGIN {RS="\r\n"; ORS=RS}
ну или -v ...

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

Если бы кому-то было интересно, как я хотел, то все было бы на другом железе и с другим ПО... =) А так я потому и написал «естественно», что ожидал такого. Тут дело даже не в ширине нового значения, а в механизме работы моего awk-скрипта, насколько я понимаю... В любом случае, спасибо!

Остальным отписавшимся также мои благодарности. В итоге сделал на основе sed'а, как посоветовали выше.

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