Додавання рядка до файлу, лише якщо він ще не існує


157

Мені потрібно додати наступний рядок до кінця файлу налаштування:

include "/configs/projectname.conf"

у файл, який називається lighttpd.conf

Я розглядаю, sedяк це зробити, але не можу розробити як.

Як я можу вставити його, лише якщо рядок уже не існує?


Якщо ви намагаєтесь відредагувати ini-файл, інструмент crudiniможе бути хорошим варіантом (але ще не для lighthttpd)
rubo77

Відповіді:


291

Просто так просто :)

grep + echo повинно вистачити:

grep -qxF 'include "/configs/projectname.conf"' foo.bar || echo 'include "/configs/projectname.conf"' >> foo.bar
  • -q будь тихо
  • -x відповідати цілій лінії
  • -F візерунок - це звичайна струна
  • https://linux.die.net/man/1/grep

Редагувати: включені пропозиції @cerin та @ thijs-wouters .


2
Я б додав перемикач -q на grep, щоб придушити вихід: grep -vq ...
Дейв Кірбі,

1
FYI, використовуючи -v та &&, не намагається зробити трюк, тоді як || без -в творів.
bPizzi

3
Це працює лише для дуже простих ліній. В іншому випадку grep інтерпретує більшість символів, які не містять альфа у вашому рядку, як шаблони, змушуючи його не виявляти рядок у файлі.
Серін

2
Прекрасне рішення. Це, звичайно, також спрацьовує на складніші вирази. Mine використовує echoдля запуску catбагаторядкового heredoc у файл конфігурації.
Ерік Л.

2
Додайте, -sщоб ігнорувати помилки, коли файл не існує, створивши новий файл саме з цим рядком.
Френк

78

Це було б чисте, читабельне та багаторазове рішення, використовуючи grepта echoдодавати рядок у файл, лише якщо його ще немає:

LINE='include "/configs/projectname.conf"'
FILE='lighttpd.conf'
grep -qF -- "$LINE" "$FILE" || echo "$LINE" >> "$FILE"

Якщо вам потрібно відповідати цілій лінії використання grep -xqF

Додайте, -sщоб ігнорувати помилки, коли файл не існує, створивши новий файл саме з цим рядком.


5
Якщо це файл, де вам потрібні права доступу до root:sudo grep -qF "$LINE" "$FILE" || echo "$LINE" | sudo tee -a "$FILE"
nebffa

Це насправді не працює, коли рядок починається з а -.
гіперкнот

@zsero: хороший момент! Я додав --у команді grep, тому він більше не буде інтерпретувати $ LINE, починаючи з тире, як опції.
rubo77

13

Ось sedверсія:

sed -e '\|include "/configs/projectname.conf"|h; ${x;s/incl//;{g;t};a\' -e 'include "/configs/projectname.conf"' -e '}' file

Якщо ваш рядок є змінною:

string='include "/configs/projectname.conf"'
sed -e "\|$string|h; \${x;s|$string||;{g;t};a\\" -e "$string" -e "}" file

Для команди, яка замінює частковий рядок на повний / оновлений запис конфігураційного файлу, або додає рядок за потребою:sed -i -e '\|session.*pam_mkhomedir.so|h; ${x;s/mkhomedir//;{g;tF};a\' -e 'session\trequired\tpam_mkhomedir.so umask=0022' -e '};:F;s/.*mkhomedir.*/session\trequired\tpam_mkhomedir.so umask=0022/g;' /etc/pam.d/common-session
bgStack15

Приємно, мені це дуже допомогло +1. Ви можете, будь ласка, пояснити своє вираження sed?
Рахуль

Я хотів би побачити пояснене пояснення цього рішення. Я використовував sedпротягом багатьох років, але в основному лише sкоманду. В holdі patternкосмічні свопи за мною.
півночі

11

Якщо ви пишете в захищений файл, відповіді @drAlberT та @ rubo77 можуть не працювати для вас, оскільки не можна судо >>. Таким же простим рішенням буде використання tee --append(або, на MacOS, tee -a):

LINE='include "/configs/projectname.conf"'
FILE=lighttpd.conf
grep -qF "$LINE" "$FILE"  || echo "$LINE" | sudo tee --append "$FILE"

6

Якщо одного дня комусь іншому доведеться мати справу з цим кодом як "застарілим кодом", тоді ця людина буде вам вдячна, якщо ви напишете менш екзотеричний код, наприклад

grep -q -F 'include "/configs/projectname.conf"' lighttpd.conf
if [ $? -ne 0 ]; then
  echo 'include "/configs/projectname.conf"' >> lighttpd.conf
fi

1
Вибачте, якщо ви не знаєте оболонки, перейдіть та адмініструйте Windows. Рішення з використанням && та || є нормальним синтаксисом оболонки, і його повинен розуміти будь-який компетентний адміністратор. Там взагалі немає нічого езотеричного.
Грем Ніколлс

3
Дякуємо за ваш коментар Грем! Так, це звичайний синтаксис оболонки. І так, будь-який компетентний адміністратор повинен це зрозуміти. Однак він працює як такий на основі побічного ефекту алгоритму оцінки виразів в оболонці. Тож ви можете назвати це все, що вам подобається, крім прямого - що було моїм початковим моментом.
Марсело Вентура


6

Ще одне рішення sed - це завжди додати його в останньому рядку та видалити попередньо існуючий.

sed -e '$a\' -e '<your-entry>' -e "/<your-entry-properly-escaped>/d"

"належним чином уникнути" означає встановити регулярний вираз, який відповідає вашому запису, тобто уникнути всіх контрольних регексів від фактичного запису, тобто поставити звороту косу рису перед ^ $ / *? + ().

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


1
Приємний однолінійний, короткий і легкий для читання. Відмінно підходить для оновлення тощо / хостів:sed -i.bak -e '$a\' -e "$NEW_IP\t\t$HOST.domain\t$HOST" -e "/.*$HOST/d" /etc/hosts
Ноам Манос

Менша версія:sed -i -e '/<entry>/d; $a <entry>'
Jérôme Pouiller

@Jezz Я думаю, що це не вдасться, якщо запис вже в кінці файлу, тому що dкоманда перезапустить програму sed і тим самим пропустить append, якщо видалення відбулося в останньому рядку.
Robin479

@ Robin479 Damned, append повинні бути виконані перед dдаліть: sed -i -e '$a<entry>' -e '/<entry>/d'. Це майже те саме, що і ваша оригінальна відповідь.
Jérôme Pouiller

3

використовувати awk

awk 'FNR==NR && /configs.*projectname\.conf/{f=1;next}f==0;END{ if(!f) { print "your line"}} ' file file

Це "файловий файл" або просто "файл"?
webdevguy

Це file fileтому , що він робить два проходи по одному файлу. NR==FNRвірно на першому проході, але не на другому. Це загальна ідіома Awk.
трійчатка

1

Відповіді за допомогою grep невірні. Вам потрібно додати опцію -x, щоб відповідати цілому рядку, інакше рядки, як #text to addі раніше, збігатимуться, коли потрібно точно додати text to add.

Тож правильне рішення - це щось на зразок:

grep -qxF 'include "/configs/projectname.conf"' foo.bar || echo 'include "/configs/projectname.conf"' >> foo.bar

1

Використання sed: вона буде вставлена ​​в кінці рядка. Ви також можете передавати змінні, як звичайно.

grep -qxF "port=9033" $light.conf
if [ $? -ne 0 ]; then
  sed -i "$ a port=9033" $light.conf
else
    echo "port=9033 already added"
fi

Використовуючи oneliner sed

grep -qxF "port=9033" $lightconf || sed -i "$ a port=9033" $lightconf

Використання echo може не працювати під коренем, але працюватиме так. Але це не дозволить вам автоматизувати речі, якщо ви хочете це зробити, оскільки це може попросити пароль.

У мене виникла проблема, коли я намагався редагувати з кореня певного користувача. Тільки додавання $usernameраніше було для мене виправкою.

grep -qxF "port=9033" light.conf
if [ $? -ne 0 ]; then
  sudo -u $user_name echo "port=9033" >> light.conf
else
    echo "already there"    
fi

Ласкаво просимо в stackoverflow! Будь ласка, уважно прочитайте запитання, перш ніж публікувати відповідь - ОП запитав, як вставити за допомогою sed
Markoorn

-1

Мені потрібно було відредагувати файл з обмеженими дозволами на запис настільки необхідними sudo. робота з відповіддю ghostdog74 та використання файлу temp:

awk 'FNR==NR && /configs.*projectname\.conf/{f=1;next}f==0;END{ if(!f) { print "your line"}} ' file > /tmp/file
sudo mv /tmp/file file

Це не виглядає правильно, він втратить інші рядки з файлу, коли лінія конфігурації відсутня.
трійчатка
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.