sed: como substituir a linha se encontrada ou anexar ao final do arquivo se não for encontrado?


34

Com um único arquivo de entrada que contém apenas comentários (começando com #) e VARIABLE = linhas de valor, é possível substituir um valor por uma única variável se encontrada e, caso contrário, anexar o par ao final do arquivo se não for encontrado?

Meu método atual funciona excluindo-o em uma primeira passagem e, em seguida, anexando-o ao final do arquivo em uma segunda passagem, mas esse método altera a ordem das linhas (e também possui dois comandos diferentes):

sed -r "/^FOOBAR=.*$/d"      -i samefile &&
sed -r "$ a\FOOBAR=newvalue" -i samefile

Existe alguma maneira de fazer isso, ie. mantendo a ordem da linha, em uma única linha sed? Se algum outro utilitário (awk, ...) fizer isso, eu assumirei o comando sed.

Respostas:


29

Na verdade, é bastante simples com sed: se uma linha corresponder, copie-a para o hespaço antigo e ssubstitua o valor.
Na $linha xfinal, altere o espaço de espera e o espaço do padrão e verifique se o último está vazio. Se não estiver vazio, significa que a substituição já foi feita, portanto não há nada a fazer. Se estiver vazio, isso significa que nenhuma correspondência foi encontrada; portanto, substitua o espaço do padrão pela variável = value desejada e, em seguida, acrescente à linha atual no buffer de espera. Finalmente, xmude novamente:

sed '/^FOOBAR=/{h;s/=.*/=newvalue/};${x;/^$/{s//FOOBAR=newvalue/;H};x}' infile

O acima é gnu sedsintaxe. Portátil:

sed '/^FOOBAR=/{
h
s/=.*/=newvalue/
}
${
x
/^$/{
s//FOOBAR=newvalue/
H
}
x
}' infile

1
Como isso seria se newvaluefosse armazenado em uma variável?
user1810087

sed "/^${varName}=/{h;s/=.*/=${varValue}/};\${x;/^$/{s//${varName}=${varValue}/;H};x}" ${VARFILE}com variáveis, aspas duplas para permitir a substituição de variáveis $$$varNamesed "/^${varName}=/{h;s/=.*/=${!varName}/};\${x;/^$/{s//${varName}=${!varName}/;H};x}" ${VARFILE}
ee

1
Esta solução não é 'simples' lol. Mas funciona. As páginas de manual do sed não são as coisas mais fáceis de analisar, seria realmente útil se você pudesse detalhar o efeito de cada seção da expressão :)
user2066480 31/07

18

Provavelmente isso pode ser reduzido. Não é um comando sed único e também usa grep, mas parece ser basicamente o que você está querendo. É uma única linha e edita o arquivo no local (sem arquivos temporários).

grep -q "^FOOBAR=" file && sed "s/^FOOBAR=.*/FOOBAR=newvalue/" -i file || 
    sed "$ a\FOOBAR=newvalue" -i file

13

Aqui está um mais simples sed abordagem , pois não acho sed hold spacefácil trabalhar. Se você estiver hold spacefamiliarizado, o uso da abordagem don_crissti oferece uma oportunidade adicional de preservar qualquer coisa da linha existente, mas isso geralmente é muito raro.

Nesta abordagem, você apenas imprime tudo, menos a linha que deseja soltar e, no final, anexa a substituição.

sed -n -e '/^FOOBAR=/!p' -e '$aFOOBAR=newvalue' infile

1
Esta versão é muito boa se você planeja atualizar um arquivo que pode ter um valor existente, mas desatualizado.
Guillem

3

Com base nas outras respostas, se o que você deseja fazer é substituir o valor de uma variável, se essa variável estiver presente no arquivo, e anexá-la ao final do arquivo, se não estiver (o que não é o que seus sedcomandos publicados fazem), você poderia tentar o seguinte:

perl -ne '$c=1 if s/^FOOBAR=.*$/FOOBAR=newvalue/;  
             print; 
             END{print "FOBAR=newvalue" unless $c==1}' file > tmpfile && 
mv tmpfile file

1

É um pouco mais fácil no awk, embora a "edição no local" não seja automática:

awk -v varname="FOOBAR" -v newval="newvalue" '
    BEGIN {FS = OFS = "="}
    $1 == varname {$2 = newval; found = 1}
    {print}
    END {if (! found) {print varname, newval}}
' file > tempfile &&
mv tempfile file

1

Basta usar grepe echopara criar um registro vazio:

grep -q '^FOOBAR=' somefile || echo 'FOOBAR=VALUE' >> somefile
sed -i 's/FOOBAR=.*$/FOOBAR=VALUE/' somefile 

Cada linha escapa com o código de erro zero.


Você perde linha ordenação ao adicionar um comandos extras e grepeecho
BlakBat

De fato, se você inserir os novos itens no final do arquivo, mas continue fazendo o pedido se substituir um valor antigo.
MUY Belgium 08/03

0

Na verdade, funciona com o seguinte: sed -i '/^FOOBAR=/{h;s/=.*/=newvalue/};${x;/^$/{s//FOOBAR=newvalue/;H};x}' infile Na resposta escolhida -i está ausente.


3
Este é realmente um comentário e não uma resposta à pergunta original. Para criticar ou solicitar esclarecimentos a um autor, deixe um comentário abaixo da postagem - você sempre pode comentar em suas próprias postagens e, quando tiver reputação suficiente , poderá comentar em qualquer post . Leia Por que preciso de 50 reputação para comentar? O que posso fazer em vez disso?
DavidPostill

-1

fazer isso com perl e no local isso funciona bem para mim:

grep ^FOOBAR= my.file && perl -i -ple "s/^FOOBAR=.+/FOOBAR=newvalue/g" my.file || echo FOOBAR=newvalue >> my.file

2
Esta resposta é ruim de muitas maneiras! grepe echoe perlem vez de fazer tudo em perl? Além disso, escrevendo em um arquivo ( >> my.file) como você está lendo ( grep my.file)?
vladr
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.