¿Cómo agregar alguna línea al final del archivo solo si aún no está allí?


10

Me gustaría editar un archivo en el lugar agregando una línea, solo si aún no existe para que mi script sea a prueba de balas.

Normalmente haría algo como:

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

También es posible hacerlo a través de ansible ( line+ insertafter=EOF+ regexp), pero es otra historia.

En vi / ex podría hacer algo como:

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

pero entonces, ¿cómo puedo verificar si la línea ya está allí (y, por lo tanto, no hacer nada) idealmente sin repetir la misma línea?

¿O tal vez hay alguna manera más fácil de hacerlo en Ex editor?


¿No puedes subcontratarlo? grep -Fq 'export PATH=~/.composer/vendor/bin:$PATH' ~/.bashrc || ex ...(o cat, para el caso)?
muru

Creo que esto podría ser algo similar a este enfoque , pero opuesto (cuando la cadena no está allí, haga algo).
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
Tenga en cuenta que exportes un comando , por lo que el resto de la línea es una palabra de shell, NO una asignación. Por lo tanto, a diferencia de una asignación variable (que no usa export), necesita comillas dobles o se romperá en espacios en blanco . Consulte también Cómo agregar correctamente una ruta a PATH .
Comodín el

1
Encontré el enlace real que estaba buscando ayer (aunque los enlaces anteriores también son buenos). ¿Se necesitan presupuestos para la asignación de variables locales?
Comodín

Respuestas:


6

Si quieres a prueba de balas , probablemente quieras algo un poco más portátil que las complicadas opciones anteriores. Me apegaría a las características de POSIXex y lo haría estúpidamente simple: simplemente elimine la línea si está allí (independientemente de cuántas veces esté allí), agréguela al final del archivo, luego guarde y salga:

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

Anclé la expresión regular para mayor robustez, aunque creo que es casi imposible hacer coincidir accidentalmente esta expresión regular. (Anclado significa usar ^y, $por lo tanto, la expresión regular solo coincidirá si coincide con una línea completa ).

Tenga en cuenta que +cmdPOSIX no requiere la sintaxis ni la característica común de permitir múltiples -cargumentos.


Hay una multitud de otras formas simples de hacer esto. Por ejemplo, agregue la línea al final del archivo, luego ejecute las dos últimas líneas del archivo a través del filtro externo UNIX uniq:

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

Esto es muy, muy limpio y simple, y también es totalmente compatible con POSIX. Utiliza la función de escapeex para el filtrado de texto externo y la utilidad de shell especificada por POSIXuniq

La conclusión es que exestá diseñada para la edición de archivos en el lugar y es, con mucho, la forma más portátil de lograrlo de una manera no interactiva. Es anterior incluso al original vi, en realidad, el nombre vies para "editor visual", y el editor no visual sobre el que se creó fue ex ).


Para una simplicidad aún mayor (y para reducirlo a una sola línea), use printfpara enviar los comandos a ex:

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


2

Una posible solución es crear una función para hacer eso:

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

Primero creamos una variable local l:resque se usará para contar el número de ocurrencias de la línea.

Usamos el globalcomando: cada vez que la línea coincide, l:resse incrementa.

Finalmente, si l:restodavía es 0, significa que la línea no aparece en el archivo, así que vamos a la última línea gracias G, y creamos una nueva línea con la oque escribimos la línea para agregar.

Nota 1 El método que utilicé para establecer el valor de l:reses un poco pesado y podría ser reemplazado por el sugerido por @Alex en su respuesta con algo como:

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

Nota 2 También para hacer que la función sea más flexible, puede usar argumentos:

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
  • Tenga en cuenta el truco con escape()para agregar un \delante de los caracteres que podría causar un problema en la búsqueda.
  • Tenga en cuenta también que aún tendrá que escapar \por sí mismo cuando llame a la función (no logré escapar \automáticamente):

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

2

Tratar:

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

Tenga en cuenta que :appendno funcionará dentro del if endifbloque con el modo Ex (vea VimDoc ), así que tengo que ponerlo norm A...afuera.


1

Normalmente uso:

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

1

Para simplificar y evitar la presencia de muchos \, la línea que se agregará será export PATH=mybin:PATH.

Estrategia principal: sustituir el mybintexto "no " (todo el archivo) con texto + definición de ruta-mybin:

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

o con sangría más didáctica :

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

Si tenemos un partido:

  1. &tiene el texto completo de bashrcy
  2. & no contiene /mybin:/

entonces:

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

-cx                                   save and leave 

ACTUALIZACIÓN : Finalmente, podemos hacer un script vim:

$ cat bashrcfix

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

chmod 755 e instálelo. Uso:bashrcfix


tenga cuidado: en \vmodo, =significa opcional


1

La mayoría de estas respuestas parecen complicadas, ¿qué hay de los buenos y viejos comandos de shell?

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

Esto podría ser fácilmente una función Bash:

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

addBashrcPath "~/.composer/vendor/bin"

EDITAR : El código de salida de grep es importante y, en este caso, debe invertirse ! grep; grep -vno saldrá como se esperaba en esta situación. Gracias @joeytwiddle por el consejo.


No creo grep -vque te dé el código de salida que deseas. Pero ! grepdebería hacerlo.
joeytwiddle

Si ese es el caso, entonces ni siquiera necesita el -vjusto !.
Sukima

Exactamente. Podrías editar tu respuesta para hacer esa corrección.
joeytwiddle

1
1. Debe usar -Fpara que dos grepno interprete los caracteres como expresiones regulares, y 2. Use en -qlugar de redirigir a /dev/null. Vea mi comentario anterior sobre la pregunta .
muru

Buenos puntos. ¿Tienes curiosidad por lo portátil que es -qy las -Fopciones? Pensé que >/dev/nullera más universal entre las diferentes plataformas (sun vs hp vs Mac OS X vs Ubuntu vs FreeBSD vs ...)
Sukima
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.