Como acrescentar alguma linha no final do arquivo apenas se ainda não estiver lá?

10

Gostaria de editar um arquivo no local anexando uma linha, apenas se ainda não existir, para tornar meu script à prova de balas.

Normalmente eu faria algo como:

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

Também é possível fazer isso via ansible ( line+ insertafter=EOF+ regexp), mas é outra história.

No vi / ex, eu poderia fazer algo como:

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

mas como verificar se a linha já está lá (e, portanto, não faço nada) idealmente sem repetir a mesma linha?

Ou talvez haja alguma maneira mais fácil de fazer isso no editor Ex?

kenorb
fonte
Você não pode terceirizar? grep -Fq 'export PATH=~/.composer/vendor/bin:$PATH' ~/.bashrc || ex ...(ou cat, nesse caso)?
muru
Eu acredito que isso poderia ser algo semelhante a essa abordagem , mas o oposto (quando a string não estiver lá, faça alguma coisa).
Kenorb # 25/15
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
Observe que exporté um comando , portanto, o restante da linha é uma palavra shell, NÃO uma atribuição. Portanto, diferentemente de uma atribuição de variável (que não usa export), você precisa de aspas duplas ou ela será quebrada no espaço em branco . Consulte também Como adicionar corretamente um caminho ao PATH .
Curinga
1
Encontrei o link real que estava procurando ontem (embora os links acima também sejam bons). As cotações são necessárias para a atribuição de variável local?
Curinga

Respostas:

6

Se você quiser ser à prova de balas , provavelmente desejará algo um pouco mais portátil do que as opções complicadas acima. Gostaria de manter os recursos do POSIXex e torná-lo estúpido-simples: basta remover a linha se ela estiver lá (independentemente de quantas vezes estiver lá), adicione-a ao final do arquivo e salve e saia:

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

Eu ancorei o regex para maior robustez, apesar de achar quase impossível coincidir acidentalmente com esse regex. (Ancorado significa usar ^e, $portanto, a regex corresponderá apenas se corresponder a uma linha inteira .)

Observe que a +cmdsintaxe não é requerida pelo POSIX, nem o recurso comum de permitir vários -cargumentos.


Existem várias outras maneiras simples de fazer isso. Por exemplo, adicione a linha ao final do arquivo e execute as duas últimas linhas do arquivo através do filtro UNIX externo uniq:

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

Isso é muito, muito limpo e simples, e também é totalmente compatível com POSIX. Ele usa o recurso Escapeex para filtragem de texto externa e o utilitário de shell especificado pelo POSIXuniq

A linha inferior é, exfoi projetada para edição de arquivos no local e é de longe a maneira mais portátil de fazer isso de maneira não interativa. Ele antecede até mesmo o original vi, na verdade, o nome vié para "editor visual", eo editor não-visual que foi construído em cima era ex .)


Para uma simplicidade ainda maior (e para reduzi-la a uma única linha), use printfpara enviar os comandos para ex:

printf %s\\n '$a' 'export PATH=~/.composer/vendor/bin:$PATH' . '$-,$!uniq' x | ex input
Curinga
fonte
Veja também: vi.stackexchange.com/q/6269/4676
curinga
2

Uma solução possível é criar uma função para fazer isso:

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

Primeiro, criamos uma variável local l:resque será usada para contar o número de ocorrências da linha.

Usamos o globalcomando: cada vez que a linha é correspondida, l:resé incrementada.

Finalmente, se l:resainda é 0, significa que a linha não aparece no arquivo, então vamos para a última linha graças a G, e criamos uma nova linha ona qual escrevemos a linha a ser adicionada.

Nota 1 O método usado para definir o valor de l:resé um pouco pesado e pode ser substituído pelo sugerido por @Alex em sua resposta com algo como:

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

Nota 2 Também para tornar a função mais flexível, você pode 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
  • Observe o truque com escape()para adicionar um \na frente dos caracteres, o que pode causar um problema na pesquisa.
  • Observe também que você ainda terá que escapar \sozinho ao chamar a função (não consegui escapar \automaticamente):

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

Experimentar:

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

Observe que :appendnão funcionará dentro do if endifbloco com o modo Ex (consulte VimDoc ), então eu tenho que colocar do norm A...lado de fora.

Alex Kroll
fonte
1

Eu normalmente uso:

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

Para simplificar e evitar a presença de muitos \, a linha a ser adicionada será export PATH=mybin:PATH.

Estratégia principal: substitua o mybintexto " não " (todos os arquivos) por texto + definição de caminho-mybin:

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

ou com mais indentação didática :

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 tivermos uma correspondência:

  1. &tem o texto completo de bashrce
  2. & não contém /mybin:/

então:

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

-cx                                   save and leave 

ATUALIZAÇÃO : Finalmente, podemos criar um script vim:

$ cat bashrcfix

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

chmod 755 e instale-o. Uso:bashrcfix


tenha cuidado: no \vmodo, =significa opcional

JJoao
fonte
1

A maioria dessas respostas parece complicada e os bons e antigos comandos do shell:

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

Essa poderia ser facilmente uma função do Bash:

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

addBashrcPath "~/.composer/vendor/bin"

EDIT : O código de saída do grep é importante e, neste caso, precisa ser revertido ! grep; grep -vnão sairá conforme o esperado nesta situação. Obrigado @joeytwiddle pela dica.

Sukima
fonte
Acho que não grep -vdá o código de saída que você deseja. Mas ! grepdeveria fazê-lo.
Joeytwiddle
Se for esse o caso, você nem precisa do -vjust the !.
Sukima 04/10/2015
Exatamente. Você pode editar sua resposta para fazer essa correção.
Joeytwiddle
1
1. Você deve usar -Fpara que grepnão interprete caracteres como expressões regulares e 2. Use em -qvez de redirecionar para /dev/null. Veja meu comentário anterior sobre a questão .
muru 6/10/2015
Bons pontos. Curioso como portátil é -qe -Fopções? Eu pensei que o >/dev/nullera mais universal entre diferentes plataformas (sol vs hp vs Mac OS X vs Ubuntu vs FreeBSD vs ...)
Sukima