Как заменить значение квадратными скобками, используя sed?

Мне нужно заменить переменные квадратными скобками в файле:

a=0
foo[1]=0
foo[2]=0

Я хочу заменить foo[1]=0 в foo[1]=2,

Чтобы изменить значения a Я использую:

func_update_value () {
     field=$1
     newvalue=$2
     sed -i "s/^\($field=\).*/\1$newvalue/" file
}

field="a"
value="2"
func_update_value $field $value

но та же команда не работает для foo[x]...

func_update_value () {
     field=$1
     newvalue=$2
     sed -i "s/^\($field=\).*/\1$newvalue/" file
}    
n=1
field="foo[$n]"
value="2"
func_update_value $field $value

Каков правильный синтаксис с sed?

Изменение сделано функцией, возможно ли использовать один sed команда для обоих случаев?

1 ответ

Решение

[1] не является (т.е. не обязательно) литералом в оболочке. Тогда это не (то есть, конечно, нет) буквально как часть шаблона регулярного выражения в sed,

Я проверил ваши предыдущие вопросы, и я думаю, что вы используете Bash. Первая часть моего ответа относится ко многим распространенным оболочкам, включая Bash (заметное исключение - zsh).


Возможная проблема в оболочке

Сначала вы назначаете значения переменным:

n=1
field="foo[$n]"
value="2"

Все это работает так, как вы ожидаете. Без кавычек 1 не является особенным, это не должно быть в кавычках. Цитируемый 2 может или не может быть процитировано, и это не будет иметь никакого значения. Двойные кавычки вокруг foo[$n] Сделать разницу; хорошо, что они есть, вот так [ а также ] ничего особенного и содержание field переменная буквально foo[1] наверняка.

Но тогда у вас есть

func_update_value $field $value

где $field не котируется Сразу после расширения параметра строка

func_update_value foo[1] 2

и затем начинается сопоставление с объектами в вашем текущем каталоге (globbing), особенно:

[…]
Соответствует любому из вложенных символов.

Это означает, что если у вас есть файл или каталог с именем foo1линия оценила бы

func_update_value foo1 2

Было бы еще хуже, если бы вы установили n=125 и у тебя было foo1 а также foo5 (И / или foo2) в каталоге. Более одного файла будет соответствовать foo[125] шаблон и окончательная форма линии будет как

func_update_value foo1 foo5 2

что, безусловно, не то, что вы хотите. Вы, вероятно, ничего не назвали foo1 в текущем каталоге, поэтому шаблон остается буквальным в этой точке и в строке

func_update_value foo[1] 2

управляет func_update_value функция с буквальными аргументами foo[1] а также 2,

Если ваш код не выходит из строя на этом этапе, это только случайно foo1 в каталоге, а не из-за правильного кодирования. Правильное кодирование включает двойные кавычки всех переменных. Есть несколько редких сценариев, в которых вы не хотите цитировать, если знаете, что делаете; Это не один из них.

Также обратите внимание, что в самой функции есть field=$1 где $1 не котируется Проблема с кавычками $1 такой же, как и без кавычек $field ранее.


Проблема в sed

Давайте предположим, что ваша функция правильно получила литерал foo[1] аргумент и его местный field переменная теперь содержит буквенную строку foo[1] как это должно.

Проблема в [1] как часть шаблона регулярного выражения в sed не буквально. Как и в оболочке, он соответствует любому из вложенных символов (за исключением того, что сопоставляется с текстом, а не с именами файлов). foo[125] будет соответствовать foo1 или же foo2 или же foo5, foo[1] Матчи foo1 только.

Делать sed матч [ а также ] буквально, вы должны избежать их в шаблоне. Вместо field="foo[$n]" тебе нужно

field="foo\[$n\]"

Можно ли использовать один sed команда для обоих случаев?

Да. Ваша текущая команда похожа sed "s/^\($field=\).*/\1$newvalue/" и это будет работать до тех пор, пока вы помните значение $field будет интерпретироваться как часть шаблона (так [, ., \(, $ и т. д. являются особенными) и ценность $newvalue будет интерпретироваться как часть замены (например, \1 это особенное). И есть разделитель, который вы выбрали (/), который не должен встречаться ни в одной из двух переменных, иначе он нарушит или изменит синтаксис.


Конечная нота

Только второе исправление (добавление обратной косой черты перед [ а также ]) даже предотвратит расширение оболочки […]Таким образом, это случайно "исправит" проблему цитирования в данном конкретном случае для данного конкретного значения переменной.

В целом, хотя вы должны применить оба исправления. И вам, безусловно, следует привыкнуть к цитированию переменных по умолчанию, это сэкономит ваше время и разочарование при отладке ваших будущих сценариев оболочки. Вот почему мой ответ не ограничивается вторым исправлением.

Другие вопросы по тегам