1

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

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

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

Для изменения значений применения I:

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

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 является специальным). И есть разделитель, который вы выбрали (/), который не должен встречаться ни в одной из двух переменных, иначе он сломает или изменит синтаксис.


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

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

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .