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


34

З єдиним вхідним файлом, який містить лише коментарі (починаючи з #) та VARIABLE = рядки значень, чи можливо замінити значення для однієї змінної, якщо вона знайдена, і, інакше, додати пару до кінця файлу, якщо вона не знайдена?

Мій поточний метод працює, видаляючи його в першому проході, потім додаючи його до кінця файлу у другому проході, але цей метод переплутує впорядкування рядків (а також є двома різними командами):

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

Чи потрібно це зробити, тобто дотримання порядку порядку в одній сед-лінії? Якщо якась інша утиліта (awk, ...) зробить це, я візьму її за sed.

Відповіді:


29

Це насправді досить просто sed: якщо рядок збігається, просто скопіюйте його в hстарий пробіл, тоді sвведіть значення.
У рядку la $t 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

1
Як це виглядатиме, якщо newvalueвін зберігається у змінній?
користувач1810087

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

1
Це рішення не є "простим" lol. Але це працює. Сторінки sed man - це не найпростіші речі для просіювання, було б дуже корисно, якби ви могли детально ознайомитись із ефектом кожного розділу виразу :)
user2066480

18

Можливо, це можна скоротити. Це не одна команда sed, і вона також використовує grep, але це, здається, в основному те, що ви хочете. Це єдиний рядок, і він редагує файл на місці (немає тимчасових файлів).

grep -q "^FOOBAR=" file && sed "s/^FOOBAR=.*/FOOBAR=newvalue/" -i file || 
    sed "$ a\FOOBAR=newvalue" -i file

13

Ось простіший sedпідхід, оскільки мені не здається sed hold spaceлегко працювати. Якщо вам подобається hold space, використання don_crissti підходу дає додаткову можливість зберегти що-небудь із існуючої лінії, але це, як правило, дуже рідко.

При такому підході ви просто роздруковуєте все, крім рядка, який ви хочете відпустити, а потім в кінці додаєте заміну.

sed -n -e '/^FOOBAR=/!p' -e '$aFOOBAR=newvalue' infile

1
Ця версія дуже хороша, якщо ви плануєте оновити файл, який може мати існуюче, але застаріле значення.
guillem

3

Виходячи з інших відповідей, якщо те, що ви хочете зробити, це замінити значення змінної, якщо ця змінна присутня у файлі, і додати її до кінця файлу, якщо її немає (що не те, що sedвиконують опубліковані команди), ви можна спробувати це:

perl -ne '$c=1 if s/^FOOBAR=.*$/FOOBAR=newvalue/;  
             print; 
             END{print "FOBAR=newvalue" unless $c==1}' file > tmpfile && 
mv tmpfile file

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 

Кожен рядок виходить з нуля коду помилки.


Ви втрачаєте впорядкування рядків, додаючи додаткові команди та grepіecho
BlakBat

Дійсно, якщо ви вставляєте нові елементи в кінці файлу, але продовжуйте впорядковувати, якщо ви замінюєте старе значення.
МУЙ Бельгія

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

2
Ця відповідь погана у багатьох варіантах! grepі echoі perlзамість того , щоб робити все , що в perl? Також запис у файл ( >> my.file), коли ви читаєте з нього ( grep my.file)?
vladr
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.