Come aggiungere una riga alla fine del file solo se non è ancora presente?


10

Vorrei modificare un file sul posto aggiungendo una riga, solo se non esiste ancora per rendere il mio script a prova di proiettile.

Normalmente farei qualcosa del tipo:

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

È anche possibile farlo tramite ansible ( line+ insertafter=EOF+ regexp), ma è un'altra storia.

In vi / ex potrei fare qualcosa del tipo:

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

ma allora come posso verificare se la linea è già lì (e quindi non fare nulla) idealmente senza ripetere la stessa linea?

O forse c'è un modo più semplice per farlo in Ex editor?


Non puoi esternalizzarlo? grep -Fq 'export PATH=~/.composer/vendor/bin:$PATH' ~/.bashrc || ex ...(o cat, del resto)?
Muru,

Credo che questo potrebbe essere qualcosa di simile a questo approccio , ma al contrario (quando la stringa non c'è, fai qualcosa).
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
Nota che exportè un comando , quindi il resto della riga è una parola shell, NON un compito. Pertanto, diversamente da un'assegnazione variabile (che non usa export), hai bisogno di virgolette doppie o si romperà su spazi bianchi . Vedi anche Come aggiungere correttamente un percorso a PATH .
Wildcard

1
Ho trovato il link effettivo che stavo cercando ieri (anche se i link sopra sono buoni anche). Sono necessarie le virgolette per l'assegnazione delle variabili locali?
Wildcard

Risposte:


6

Se vuoi antiproiettile , probabilmente vuoi qualcosa di un po 'più portatile delle complicate opzioni sopra. Vorrei attenermi alle funzionalità POSIX diex e renderlo stupido-semplice: basta rimuovere la linea se è lì (indipendentemente da quante volte è lì), aggiungerla alla fine del file, quindi salvare ed uscire:

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

Ho ancorato il regex per una maggiore robustezza, anche se penso che sia quasi impossibile abbinare accidentalmente questo regex. (Ancorato significa usare ^e $quindi il regex corrisponderà solo se corrisponde a un'intera riga.)

Si noti che la +cmdsintassi non è richiesta da POSIX, né è la caratteristica comune di consentire più -cargomenti.


Ci sono molti altri modi semplici per farlo. Ad esempio, aggiungi la riga alla fine del file, quindi esegui le ultime due righe del file attraverso il filtro UNIX esterno uniq:

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

Questo è molto semplice e pulito, ed è anche completamente conforme a POSIX. Utilizza la funzione Escape diex per il filtro di testo esterno e l' utilità di shell specificata da POSIXuniq

La linea di fondo è, exè progettata per la modifica dei file sul posto ed è di gran lunga il modo più portatile per farlo in modo non interattivo. Precede persino l'originale vi, in realtà - il nome viè per "editor visivo", e l'editor non visivo su cui è stato costruito lo era ex .)


Per una semplicità ancora maggiore (e per ridurla a una sola riga), utilizzare printfper inviare i comandi a ex:

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


2

Una possibile soluzione è creare una funzione per farlo:

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

Innanzitutto creiamo una variabile locale l:resche verrà utilizzata per contare il numero di occorrenze della linea.

Usiamo il globalcomando: ogni volta che la linea viene abbinata, l:resviene incrementata.

Infine se l:resè ancora 0 significa che la riga non appare nel file, quindi andiamo all'ultima riga grazie Ge creiamo una nuova riga con la oquale scrivere la riga da aggiungere.

Nota 1 Il metodo che ho usato per impostare il valore di l:resè un po 'pesante e potrebbe essere sostituito da quello suggerito da @Alex nella sua risposta con qualcosa di simile:

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

Nota 2 Anche per rendere la funzione più flessibile è possibile utilizzare gli argomenti:

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
  • Nota il trucco con escape()l'aggiunta di un \davanti ai caratteri che potrebbe causare un problema nella ricerca.
  • Nota anche che dovrai comunque scappare \da solo quando chiami la funzione (non sono riuscito a scappare \automaticamente):

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

2

Provare:

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

Nota che :appendnon funzionerà all'interno del if endifblocco con la modalità Ex (vedi VimDoc ) quindi devo metterlo norm A...all'esterno.


1

Di solito uso:

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

1

Al fine di semplificare ed evitare la presenza di molti \, sarà la linea da aggiungere export PATH=mybin:PATH.

Strategia principale: sostituire il mybintesto "non- " (tutto il file) con text + mybin-path-definition:

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

o con più rientri didattici :

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

Se abbiamo una corrispondenza:

  1. &ha il testo completo di bashrce
  2. & non contiene /mybin:/

così:

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

-cx                                   save and leave 

AGGIORNAMENTO : Infine, possiamo creare uno script vim:

$ cat bashrcfix

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

chmod 755 e installarlo. Uso:bashrcfix


fai attenzione: in \vmodalità, =significa facoltativo


1

La maggior parte di queste risposte sembrano complicate come su buoni vecchi comandi shell:

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

Questa potrebbe essere facilmente una funzione Bash:

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

addBashrcPath "~/.composer/vendor/bin"

EDIT : il codice di uscita di grep è importante e in questo caso deve essere invertito ! grep; grep -vnon uscirà come previsto in questa situazione. Grazie @joeytwiddle per l'informazione.


Non credo grep -vti dia il codice di uscita che desideri. Ma ! grepdovrebbe farlo.
joeytwiddle,

In tal caso, non è nemmeno necessario il -vsolo !.
Sukima,

Esattamente. È possibile modificare la risposta per apportare tale correzione.
joeytwiddle,

1
1. Dovresti usare in -Fmodo che i grepdos non interpretino i caratteri come espressioni regolari e 2. Usa -qinvece di reindirizzare a /dev/null. Vedi il mio precedente commento sulla domanda .
muru,

Punti buoni. Curioso di quanto sia portatile -qe -Fopzioni? Ho pensato che >/dev/nullfosse più universale tra le diverse piattaforme (sun vs hp vs Mac OS X vs Ubuntu vs FreeBSD vs ...)
Sukima
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.