Wie kann eine Zeile am Ende der Datei nur angehängt werden, wenn sie noch nicht vorhanden ist?


10

Ich möchte eine Datei direkt durch Anhängen einer Zeile bearbeiten, nur wenn sie noch nicht vorhanden ist, um mein Skript kugelsicher zu machen.

Normalerweise würde ich so etwas machen wie:

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

Es ist auch möglich, dies über ansible ( line+ insertafter=EOF+ regexp) zu tun , aber es ist eine andere Geschichte.

In vi / ex könnte ich so etwas machen wie:

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

aber wie überprüfe ich dann im Idealfall, ob die Leitung bereits vorhanden ist (und tue somit nichts), ohne dieselbe Zeile zu wiederholen?

Oder gibt es eine einfachere Möglichkeit, dies im Ex-Editor zu tun?


Können Sie es nicht auslagern? grep -Fq 'export PATH=~/.composer/vendor/bin:$PATH' ~/.bashrc || ex ...(oder auch cat)?
Muru

Ich glaube, dies könnte etwas Ähnliches wie dieser Ansatz sein , aber entgegengesetzt (wenn die Zeichenfolge nicht vorhanden ist, tun Sie etwas).
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
Beachten Sie, dass dies exportein Befehl ist. Der Rest der Zeile ist also ein Shell-Wort, keine Zuweisung. Daher benötigen Sie im Gegensatz zu einer Variablenzuweisung (die nicht verwendet wird export) doppelte Anführungszeichen, da diese sonst im Leerzeichen unterbrochen wird . Siehe auch So fügen Sie PATH einen Pfad korrekt hinzu .
Wildcard

1
Ich habe den tatsächlichen Link gefunden, nach dem ich gestern gesucht habe (obwohl die obigen Links auch gut sind). Werden Anführungszeichen für die Zuweisung lokaler Variablen benötigt?
Wildcard

Antworten:


6

Wenn Sie kugelsicher sein möchten , möchten Sie wahrscheinlich etwas, das etwas tragbarer ist als die oben genannten komplizierten Optionen. Ich würde mich an die POSIX-Funktionen von haltenex und es einfach machen: Entfernen Sie einfach die Zeile, wenn sie vorhanden ist (unabhängig davon, wie oft sie vorhanden ist), fügen Sie sie am Ende der Datei hinzu, speichern Sie sie und beenden Sie sie:

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

Ich verankert die Regex für zusätzliche Robustheit, obwohl ich denke , dass es fast unmöglich ist, aus Versehen diese Regex. (Verankert bedeutet Verwenden ^und $daher stimmt der reguläre Ausdruck nur überein, wenn er mit einer ganzen Zeile übereinstimmt .)

Beachten Sie, dass die +cmdSyntax weder von POSIX benötigt wird noch das gemeinsame Merkmal, mehrere -cArgumente zuzulassen .


Es gibt eine Vielzahl anderer einfacher Möglichkeiten, dies zu tun. Fügen Sie beispielsweise die Zeile am Ende der Datei hinzu und führen Sie die letzten beiden Zeilen der Datei über den externen UNIX-Filter aus uniq:

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

Dies ist sehr, sehr sauber und einfach und auch vollständig POSIX-konform. Es verwendet die Escape-Funktion vonex für die externe Textfilterung und das von POSIX angegebene Shell-Dienstprogrammuniq

Unterm Strich ist, exist entworfen für in-Place - Datei bearbeiten , und es ist bei weitem die meisten tragbare Art und Weise zu erreichen , dass in einer nicht-interaktiven Art und Weise. Es ist sogar älter als das Original vi- der Name visteht für "Visual Editor" und der nicht-visuelle Editor, auf dem es aufgebaut wurde, war ex .)


Verwenden Sie zur noch größeren Vereinfachung (und um es auf eine einzelne Zeile zu reduzieren) printfdie Befehle an ex:

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


2

Eine mögliche Lösung besteht darin, eine Funktion zu erstellen, um dies zu tun:

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

Zuerst erstellen wir eine lokale Variable, mit l:resder die Anzahl der Vorkommen der Zeile gezählt wird.

Wir verwenden den globalBefehl: Jedes Mal, wenn die Zeile übereinstimmt, l:reswird sie erhöht.

Wenn schließlich l:resnoch 0 ist, bedeutet dies, dass die Zeile nicht in der Datei angezeigt wird, sodass wir dank zur letzten Zeile wechseln Gund eine neue Zeile erstellen, mit oder wir die hinzuzufügende Zeile schreiben.

Anmerkung 1 Die Methode, mit der ich den Wert von festgelegt habe, l:resist etwas schwer und kann durch die von @Alex in seiner Antwort vorgeschlagene ersetzt werden :

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

Hinweis 2 Um die Funktion flexibler zu gestalten, können Sie folgende Argumente verwenden:

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
  • Beachten Sie den Trick mit escape()dem Hinzufügen eines \vor den Zeichen, was zu Problemen bei der Suche führen kann.
  • Beachten Sie auch, dass Sie \beim Aufrufen der Funktion immer noch selbst entkommen müssen (ich habe es nicht geschafft, \automatisch zu entkommen ):

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

2

Versuchen:

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

Beachten Sie, dass der Innenblock im Ex-Modus :appendnicht funktioniert if endif(siehe VimDoc ), daher muss ich ihn nach norm A...außen stellen.


1

Ich benutze normalerweise:

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

1

Um die Anwesenheit vieler zu vereinfachen und zu vermeiden \, wird die hinzuzufügende Zeile verwendet export PATH=mybin:PATH.

Hauptstrategie: Ersetzen Sie den "Nicht- mybin" Text (alle Dateien) durch Text + Mybin-Pfad-Definition:

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

oder mit mehr didaktischer Einrückung :

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

Wenn wir ein Match haben:

  1. &hat den vollständigen Text von bashrcund
  2. & beinhaltet nicht /mybin:/

damit:

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

-cx                                   save and leave 

UPDATE : Endlich können wir ein Vim-Skript erstellen:

$ cat bashrcfix

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

chmod 755 und installiere es. Verwendungszweck:bashrcfix


Vorsicht: Im \vModus =bedeutet optional


1

Die meisten dieser Antworten scheinen kompliziert zu sein, wie wäre es mit guten alten Shell-Befehlen:

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

Dies könnte leicht eine Bash-Funktion sein:

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

addBashrcPath "~/.composer/vendor/bin"

BEARBEITEN : Der Exit-Code von grep ist wichtig und muss in diesem Fall umgekehrt verwendet werden ! grep. grep -vwird in dieser Situation nicht wie erwartet beendet. Danke @joeytwiddle für den Tipp.


Ich glaube nicht, dass grep -vSie den gewünschten Exit-Code erhalten. Sollte ! grepes aber tun.
Joeytwiddle

Wenn das der Fall ist, brauchen Sie nicht einmal das -vnur das !.
Sukima

Genau. Sie können Ihre Antwort bearbeiten, um diese Korrektur vorzunehmen.
Joeytwiddle

1
1. Sie sollten verwenden, -Fdamit grepZeichen nicht als reguläre Ausdrücke interpretiert werden, und 2. Verwenden Sie, -qanstatt umzuleiten /dev/null. Siehe meinen früheren Kommentar zur Frage .
Muru

Gute Argumente. Neugierig, wie tragbar ist -qund welche -FOptionen? Ich dachte, das wäre >/dev/nulluniverseller unter verschiedenen Plattformen (Sonne gegen HP gegen Mac OS X gegen Ubuntu gegen FreeBSD gegen ...)
Sukima
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.