sed: как заменить строку, если найдена, или добавить в конец файла, если не найдена?

33

С одним входным файлом, который содержит только комментарии (начиная с #) и строки VARIABLE = значение, возможно ли заменить значение для одной переменной, если оно найдено, и, в противном случае, добавить пару в конец файла, если не найдено?

Мой текущий метод работает, удаляя его в первом проходе, затем добавляя его в конец файла во втором проходе, но этот метод портит порядок строк (и это также две разные команды):

sed -r "/^FOOBAR=.*$/d"      -i samefile &&
sed -r "$ a\FOOBAR=newvalue" -i samefile

Есть ли в любом случае, чтобы сделать это, т.е. поддерживать порядок строк в одной строке sed? Если какая-то другая утилита (awk, ...) сделает это, я возьму это на себя.

BlakBat
источник

Ответы:

29

Это на самом деле довольно просто sed: если строка соответствует, просто скопируйте ее в hстарое пространство, затем sзамените значение.
В конце $строки xизмените удерживающее пространство и пространство образца, затем проверьте, является ли последнее пустым. Если он не пустой, значит, замена уже сделана, так что ничего не поделать. Если он пуст, это означает, что совпадение не найдено, поэтому замените пространство шаблона на требуемую переменную = значение, а затем добавьте к текущей строке в буфере удержания. Наконец, xснова изменим:

sed '/^FOOBAR=/{h;s/=.*/=newvalue/};${x;/^$/{s//FOOBAR=newvalue/;H};x}' infile

Выше gnu sedсинтаксис. Портативный:

sed '/^FOOBAR=/{
h
s/=.*/=newvalue/
}
${
x
/^$/{
s//FOOBAR=newvalue/
H
}
x
}' infile
don_crissti
источник
1
Как это будет выглядеть, если newvalueхранится в переменной?
user1810087
sed "/^${varName}=/{h;s/=.*/=${varValue}/};\${x;/^$/{s//${varName}=${varValue}/;H};x}" ${VARFILE}с переменными, двойные кавычки, чтобы обеспечить подстановку переменных и экранирование а $, которое не является подстановкой в ​​данный момент, дополнительно, если ваше значение хранится в $$varName:sed "/^${varName}=/{h;s/=.*/=${!varName}/};\${x;/^$/{s//${varName}=${!varName}/;H};x}" ${VARFILE}
DarkMukke
Это решение не просто, лол. Но это работает. Страницы man sed - это не самая простая вещь, которую нужно просеять, было бы очень полезно, если бы вы могли детализировать эффект каждого раздела выражения :)
user2066480
18

Это, вероятно, может быть сокращено. Это не одна команда sed, и она также использует grep, но, похоже, это именно то, что вам нужно. Это одна строка, и она редактирует файл на месте (без временных файлов).

grep -q "^FOOBAR=" file && sed "s/^FOOBAR=.*/FOOBAR=newvalue/" -i file || 
    sed "$ a\FOOBAR=newvalue" -i file
snapshoe
источник
11

Вот более простой sedподход, так как мне sed hold spaceнелегко работать с ним. Если вам удобно hold space, использование подхода don_crissti дает дополнительную возможность сохранить что-либо из существующей линии, но это обычно очень редко.

При таком подходе вы просто печатаете все, кроме линии, которую хотите отбросить, а затем в конце добавляете замену.

sed -n -e '/^FOOBAR=/!p' -e '$aFOOBAR=newvalue' infile
haridsv
источник
1
Эта версия очень хороша, если вы планируете обновить файл, который может иметь существующее, но устаревшее значение.
Гиллем
3

Основываясь на других ответах, если вы хотите заменить значение переменной, если эта переменная присутствует в файле, и добавить его в конец файла, если его нет (это не то, что делают ваши опубликованные sedкоманды), вы мог бы попробовать это:

perl -ne '$c=1 if s/^FOOBAR=.*$/FOOBAR=newvalue/;  
             print; 
             END{print "FOBAR=newvalue" unless $c==1}' file > tmpfile && 
mv tmpfile file
terdon
источник
1

В awk это немного проще, хотя «редактирование на месте» не происходит автоматически:

awk -v varname="FOOBAR" -v newval="newvalue" '
    BEGIN {FS = OFS = "="}
    $1 == varname {$2 = newval; found = 1}
    {print}
    END {if (! found) {print varname, newval}}
' file > tempfile &&
mv tempfile file
Гленн Джекман
источник
1

Просто используйте grepи echoдля создания пустой записи:

grep -q '^FOOBAR=' somefile || echo 'FOOBAR=VALUE' >> somefile
sed -i 's/FOOBAR=.*$/FOOBAR=VALUE/' somefile 

Каждая строка сбрасывается с нулевым кодом ошибки.

MUY Бельгия
источник
Вы теряете порядок строк при добавлении дополнительных команд grepиecho
BlakBat
Действительно, если вы вставляете новые элементы в конец файла, но сохраняйте порядок, если вы заменяете старое значение.
MUY Бельгия
0

На самом деле работает со следующим: sed -i '/^FOOBAR=/{h;s/=.*/=newvalue/};${x;/^$/{s//FOOBAR=newvalue/;H};x}' infile В ответе выбран -i отсутствует.

Йогеш Камат
источник
3
Это действительно комментарий, а не ответ на оригинальный вопрос. Чтобы критиковать или запрашивать разъяснения у автора, оставьте комментарий под его постом - вы всегда можете комментировать свои собственные посты, и, когда у вас будет достаточно репутации, вы сможете комментировать любые посты . Пожалуйста, прочитайте Почему мне нужно 50 репутации, чтобы комментировать? Что я могу сделать вместо этого?
DavidPostill
-1

чтобы сделать это с Perl и на месте это прекрасно работает для меня:

grep ^FOOBAR= my.file && perl -i -ple "s/^FOOBAR=.+/FOOBAR=newvalue/g" my.file || echo FOOBAR=newvalue >> my.file
antweiss
источник
2
Этот ответ плох по многим причинам! grepи echoи perlвместо того , чтобы делать все , что в perl? Кроме того, запись в файл ( >> my.file), как вы читаете из него ( grep my.file)?
Влад