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


10

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

Зазвичай я б робив щось на кшталт:

cat >> ~/.bashrc <<EOF
export PATH=~/.composer/vendor/bin:\$PATH
EOF

Це також можна зробити за допомогою ansible ( line+ insertafter=EOF+ regexp), але це вже інша історія.

У vi / ex я міг би зробити щось на кшталт:

ex +'$s@$@\rexport PATH=\~/.composer/vendor/bin:$PATH@' -cwq ~/.bashrc

але як потім я перевірити, чи рядок вже є (і, отже, нічого не робити) в ідеалі, не повторюючи ту саму лінію?

А може, є якийсь простіший спосіб зробити це в редакторі Ex?


Ви не можете це передавати в аутсорсинг? grep -Fq 'export PATH=~/.composer/vendor/bin:$PATH' ~/.bashrc || ex ...(або cat, з цього приводу)?
muru

Я вважаю, що це може бути щось подібне до цього підходу , але навпаки (коли рядка немає, роби щось).
kenorb

ex ~/.bashrc -c "if search('export PATH=\~\/.composer\/vendor\/bin:\$PATH')>0 | norm quit | endif | norm Aexport PATH=~/.composer/vendor/bin:$PATH"
Алекс Кролл

1
Зверніть увагу, що exportце команда , тому решта рядка - це слово оболонки, а не призначення. Тому на відміну від присвоєння змінної (яка не використовується export), вам потрібні подвійні лапки, інакше вона порушиться на пробілі . Також див. Як правильно додати шлях до PATH .
Wildcard

1
Я знайшов фактичне посилання, яке я шукав вчора (хоча вищезгадані посилання також хороші). Чи потрібні лапки для призначення місцевих змінних?
Wildcard

Відповіді:


6

Якщо ви хочете куленепробивна , ви, мабуть, хочете чогось трохи більш портативного, ніж складні варіанти вище. Я б дотримувався POSIX-функційex і робив це нерозумно простим: просто видаліть рядок, якщо він є там (незалежно від того, скільки разів він є), додайте його в кінець файлу, а потім збережіть та вийдіть:

ex -sc 'g/^export PATH=\~\/\.composer\/vendor\/bin:\$PATH$/d
$a
export PATH=~/.composer/vendor/bin:$PATH
.
x' ~/.bashrc

Я закріпив регулярний вираз для додаткової стійкості, хоча думаю, що практично неможливо випадково співставити цей регекс. (Прив’язаний означає використання, ^і $тому регулярний вираз буде відповідати лише у тому випадку, якщо він відповідає цілому рядку.)

Зауважте, що +cmdPOSIX не вимагає синтаксису, а також загальною ознакою дозволу декількох -cаргументів.


Існує безліч інших простих способів зробити це. Наприклад, додайте рядок до кінця файлу, а потім запустіть останні два рядки файлу через зовнішній фільтр UNIX uniq:

ex -sc '$a
export PATH=~/.composer/vendor/bin:$PATH
.
$-,$!uniq
x' input

Це дуже дуже чисто і просто, а також повністю сумісне з POSIX. Він використовує функцію Escapeex для зовнішньої фільтрації тексту та утиліту оболонки, вказану POSIXuniq

Суть полягає в exтому, що призначена для редагування файлів на місці, і це, безумовно, самий портативний спосіб досягти цього неінтерактивним способом. Він передує навіть оригінальне vi, на Насправді, назва viдля «візуального редактора», а не візуальний редактор був побудований на була ex .)


Для ще більшої простоти (і щоб зменшити її до одного рядка) використовуйте printfдля надсилання команд на ex:

printf %s\\n '$a' 'export PATH=~/.composer/vendor/bin:$PATH' . '$-,$!uniq' x | ex input


2

Можливим рішенням є створення функції для цього:

function! AddLine()

    let l:res = 0
    g/export PATH=\~\/.composer\/vendor\/bin:\\\$PATH/let l:res = l:res+1

    if (l:res == 0)
        normal G
        normal oexport PATH=~/.composer/vendor/bin:\$PATH
    endif

endfunction

Спочатку ми створюємо локальну змінну, l:resяка буде використовуватися для підрахунку кількості вхідних рядків.

Ми використовуємо globalкоманду: кожен раз, коли рядок збігається, l:resзбільшується.

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

Примітка 1 Метод, який я використовував для встановлення значення, l:resтрохи важкий і його можна замінити тим, що запропонував @Alex у своїй відповіді чимось на кшталт:

let l:res = search('export PATH=\~\/.composer\/vendor\/bin:\\\$PATH')

Примітка 2 Також, щоб зробити функцію гнучкішою, ви можете використовувати аргументи:

function! AddLine(line)

    let l:line =  escape(a:line, "/~$" )

    let l:res = 0
    exe "g/" . l:line . "/let l:res = l:res+1"

    if (l:res == 0)
        normal G
        exe "normal o" . a:line
    endif

endfunction
  • Зверніть увагу на хитрість escape()додати \символи перед символами, що може спричинити проблеми в пошуку.
  • Зауважте також, що вам все одно доведеться уникати \виклику при виклику функції (мені не вдалося \автоматично вийти ):

    call AddLine("export PATH=~/.composer/vendor/bin:\\$PATH")
    

2

Спробуйте:

ex ~/.bashrc -c "if search('export PATH=\~\/.composer\/vendor\/bin:\$PATH')>0 | norm quit | endif | norm Aexport PATH=~/.composer/vendor/bin:$PATH"

Зауважте, що :appendне буде працювати всередині if endif-блок в режимі Ex (див. VimDoc ), тому мені доведеться поставити norm A...зовні.


1

Я зазвичай використовую:

perl -i -p0e '$_.="export PATH=~/bin:\$PATH\n" unless m!~/bin:!' ~/.bashrc

1

Щоб спростити та уникнути присутності багатьох \, буде доданий рядок export PATH=mybin:PATH.

Основна стратегія: mybinпідмініть "не " текст (весь файл) текстом + визначення mybin-path:

 vim  +'s#\v%^((.|\n)(mybin:)@!)*%$#&\rexport PATH=mybin:PATH#e' -cx bashrc

або з більш дидактичним відступом :

s#                                    substitute
   \v                                  very magical to redude \

   %^                                  file begin
   ((.|\n)(mybin:)@!)*                 multi-line str without "mybin:"
                                         (any char not followed by "mybin")*
   %$                                  end of file
 #                                    by

Якщо у нас є збіг:

  1. &містить повний текст bashrcта
  2. & не містить /mybin:/

тому:

 #                                    by ...
   &                                   all file
   export PATH=mybin:PATH              and the new path
 #e                                   don't complain if patt not found

-cx                                   save and leave 

ОНОВЛЕННЯ : Нарешті, ми можемо зробити сценарій vim:

$ cat bashrcfix

#!/usr/bin/vim -s
:e ~/fakebashrc
:s#\v%^((.|\n)(mybin:)@!)*%$#&\rexport PATH=mybin:PATH#e
:x

chmod 755 та встановіть його. Використання:bashrcfix


будьте обережні: у \vрежимі =означає необов'язково


1

Більшість із цих відповідей здаються складними щодо старих хороших команд оболонки:

grep composer/vender ~/.bashrc || cat >> ~/.bashrc <<EOF
export PATH=~/.composer/vendor/bin:\$PATH
EOF

Це легко може бути функцією Bash:

addBashrcPath() {
  if ! grep "$1" ~/.bashrc >/dev/null 2>&1; then
    printf 'export PATH=%s:$PATH' "$1" >> ~/.bashrc
  fi
}

addBashrcPath "~/.composer/vendor/bin"

EDIT : вихідний код grep важливий, і в цьому випадку його потрібно використовувати в зворотному порядку ! grep; grep -vне вийде, як очікувалося в цій ситуації. Дякую @joeytwiddle за пораду.


Я не думаю, що grep -vви надаєте потрібний вам вихідний код. Але ! grepслід це робити.
joeytwiddle

Якщо це так, то вам навіть не потрібно -vпросто !.
Сукіма

Саме так. Ви можете змінити свою відповідь, щоб внести виправлення.
joeytwiddle

1
1. Ви повинні використовувати -Fтак, щоб не grepінтерпретувати символи як звичайні вирази, і 2. Використовувати -qзамість перенаправлення на /dev/null. Дивіться мій раніше коментар до питання .
муру

Хороші бали. Цікаво, наскільки портативні -qта -Fваріанти? Я думав, що >/dev/nullбільш універсальний серед різних платформ (сонце проти HP проти Mac OS X проти Ubuntu проти FreeBSD проти…)
Sukima
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.