Jak dodać linię na końcu pliku tylko wtedy, gdy jeszcze go nie ma?

10

Chciałbym edytować plik w miejscu, dodając wiersz, tylko jeśli jeszcze nie istnieje, aby mój skrypt był kuloodporny.

Normalnie zrobiłbym coś takiego:

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

Można to również zrobić za pomocą ansible ( line+ insertafter=EOF+ regexp), ale to inna historia.

W vi / ex mogłem zrobić coś takiego:

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

ale jak mogę sprawdzić, czy linia już tam jest (a więc nic nie robić) idealnie bez powtarzania tej samej linii?

A może istnieje prostszy sposób na zrobienie tego w edytorze Ex?

kenorb
źródło
Nie możesz tego zlecić na zewnątrz? grep -Fq 'export PATH=~/.composer/vendor/bin:$PATH' ~/.bashrc || ex ...(lub cat, jeśli o to chodzi)?
muru
Wierzę, że może to być coś podobnego do tego podejścia , ale odwrotnie (jeśli nie ma łańcucha, zrób coś).
kenorb
ex ~/.bashrc -c "if search('export PATH=\~\/.composer\/vendor\/bin:\$PATH')>0 | norm quit | endif | norm Aexport PATH=~/.composer/vendor/bin:$PATH"
Alex Kroll
1
Zauważ, że exportjest to polecenie , więc reszta wiersza jest słowem powłoki, a NIE przypisaniem. Dlatego, inaczej niż w przypadku przypisania zmiennej (która nie korzysta export), potrzebujesz podwójnych cudzysłowów, w przeciwnym razie spowoduje to przerwanie białych znaków . Zobacz także Jak poprawnie dodać ścieżkę do ŚCIEŻKI .
Wildcard
1
Znalazłem właściwy link, którego szukałem wczoraj (chociaż powyższe linki są również dobre). Czy do przypisania zmiennych lokalnych potrzebne są oferty?
Wildcard,

Odpowiedzi:

6

Jeśli chcesz kuloodporny , prawdopodobnie potrzebujesz czegoś bardziej przenośnego niż skomplikowane opcje powyżej. Chciałbym trzymać się funkcji POSIXex i uczynić to głupio prostym: po prostu usuń linię, jeśli tam jest (niezależnie od tego, ile razy tam jest), dodaj ją na końcu pliku, a następnie zapisz i wyjdź:

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

Zakotwicziłem regex dla dodatkowej solidności, chociaż myślę, że prawie niemożliwe jest przypadkowe dopasowanie tego regexu. (Zakotwiczone za pomocą środków ^i $tak regex będzie tylko dopasować jeśli odpowiada on całą linię).

Zauważ, że +cmdskładnia nie jest wymagane przez POSIX, ani wspólna cecha pozwala wielu -cargumentów.


Można to zrobić na wiele innych prostych sposobów. Na przykład dodaj wiersz na końcu pliku, a następnie uruchom dwa ostatnie wiersze pliku przez zewnętrzny filtr UNIX uniq:

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

Jest to bardzo czyste i proste, a także w pełni zgodne z POSIX. Wykorzystuje funkcję Escapeex dla zewnętrznego filtrowania tekstu oraz narzędzie powłoki określone przez POSIXuniq

Najważniejsze jest to, że exjest przeznaczony do edycji plików w miejscu i jest zdecydowanie najbardziej przenośnym sposobem na osiągnięcie tego w sposób nieinteraktywny. To wyprzedza nawet oryginalne vi, faktycznie, nazwa vijest „wizualnego edytora”, a redaktor non-wideo został zbudowany na to ex ).


Aby jeszcze bardziej uprościć (i zredukować go do jednej linii), użyj printfpolecenia wysyłania poleceń do ex:

printf %s\\n '$a' 'export PATH=~/.composer/vendor/bin:$PATH' . '$-,$!uniq' x | ex input
Dzika karta
źródło
Powiązane: vi.stackexchange.com/q/6269/4676
Wildcard
2

Możliwym rozwiązaniem jest utworzenie funkcji, aby to zrobić:

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

Najpierw tworzymy zmienną lokalną, l:resktóra będzie używana do zliczania liczby wystąpień linii.

Używamy globalpolecenia: za każdym razem, gdy linia jest dopasowywana, l:resjest zwiększana.

Na koniec, jeśli l:resnadal wynosi 0, oznacza to, że linia nie pojawia się w pliku, dzięki czemu przechodzimy do ostatniej linii Gi tworzymy nową linię, ona której piszemy linię do dodania.

Uwaga 1 Metoda, której użyłem do ustawienia wartości, l:resjest nieco ciężka i można ją zastąpić metodą sugerowaną przez @Alex w odpowiedzi na coś takiego:

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

Uwaga 2 Aby uelastycznić funkcję, możesz użyć argumentów:

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
  • Zwróć uwagę na sztuczkę polegającą na escape()dodaniu \przed znakami znaku, który może powodować problemy podczas wyszukiwania.
  • Pamiętaj również, że nadal będziesz musiał sam uciec \podczas wywoływania funkcji (nie udało mi się uciec \automatycznie):

    call AddLine("export PATH=~/.composer/vendor/bin:\\$PATH")
    
statox
źródło
2

Próbować:

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

Zauważ, że :appendnie będzie działać wewnątrz if endif-bloku w trybie Ex (patrz VimDoc ), więc muszę norm A...wyjść na zewnątrz.

Alex Kroll
źródło
1

Zwykle używam:

perl -i -p0e '$_.="export PATH=~/bin:\$PATH\n" unless m!~/bin:!' ~/.bashrc
JJoao
źródło
1

Aby uprościć i uniknąć obecności wielu \, dodawana będzie linia export PATH=mybin:PATH.

Główna strategia: mybinzastąp tekst „nie- ” (cały plik) tekstem + definicja ścieżki mybin:

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

lub z większym wcięciem dydaktycznym :

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

Jeśli mamy dopasowanie:

  1. &ma pełny tekst bashrci
  2. & nie zawiera /mybin:/

więc:

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

-cx                                   save and leave 

AKTUALIZACJA : Wreszcie możemy stworzyć skrypt vim:

$ cat bashrcfix

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

chmod 755 i zainstaluj go. Stosowanie:bashrcfix


bądź ostrożny: w \vtrybie =oznacza opcjonalne

JJoao
źródło
1

Większość z tych odpowiedzi wydaje się skomplikowana, co powiesz na dobre stare polecenia powłoki:

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

Może to być z łatwością funkcja Bash:

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

addBashrcPath "~/.composer/vendor/bin"

EDYCJA : Kod wyjścia grep jest ważny iw tym przypadku należy go odwrócić ! grep; grep -vnie wyjdzie zgodnie z oczekiwaniami w tej sytuacji. Dziękuję @joeytwiddle za wskazówkę.

Sukima
źródło
Nie sądzę, że grep -vdaje ci kod wyjścia, który chcesz. Ale ! greppowinienem to zrobić.
joeytwiddle
W takim przypadku nie potrzebujesz nawet -vtego !.
Sukima
Dokładnie. Możesz edytować swoją odpowiedź, aby wprowadzić poprawkę.
joeytwiddle
1
1. Powinieneś używać -F, aby grepnie interpretować znaków jako wyrażeń regularnych, i 2. Używać -qzamiast przekierowywać na /dev/null. Zobacz mój wcześniejszy komentarz do pytania .
muru
Słuszne uwagi. Ciekawy, jak przenośny jest -qi jakie -Fopcje? Myślałem, że >/dev/nulljest bardziej uniwersalny na różnych platformach (słońce vs hp vs Mac OS X vs Ubuntu vs FreeBSD vs…)
Sukima,