Gerenciando expressões muito complexas com sed

1

Estou tentando escrever um script que substitua essa linha:

 [ "$PS1" = "\s-\v\\$ " ] && PS1="[\u@\h \W]\$ "

com essa linha:

# [ "$PS1" = "\s-\v\\$ " ] && PS1="[\u@\h \W]\$ "

e isso adicionará essa nova linha:

[ "$PS1" = "\s-\v\\$ " ] && PS1="[\u@\h \D{%T} \W]\$ "

para o arquivo /etc/bashrc . Basicamente, eu quero que o script comente as configurações antigas e adicione um timestamp ao lado do nome de usuário no prompt (a nova configuração). Eu tentei fazer a primeira parte assim:

pattern=' [ "$PS1" = "\s-\v\\$ " ] && PS1="[\u@\h \W]\$ "'
sudo sed 's/${pattern}/#${pattern}/' < /etc/bashrc 

Mas não funcionou, acho que é por causa dos caracteres especiais na string. Mas não tenho certeza de quais caracteres eu preciso escapar. A última parte deveria ser algo assim:

sed -i '  [ "$PS1" = "\s-\v\\$ " ] && PS1="[\u@\h \D{%T} \W]\$ "' /etc/bashrc

Aprovará alguma orientação.

Obrigado

    
por lakerda 06.02.2018 / 13:58

3 respostas

2
  1. As aspas simples no script sed impedem que o ${pattern} seja expandido. Você teria que usar aspas duplas (mas isso levaria ao problema mencionado com caracteres especiais)
  2. Use sed endereçando com o suficiente da linha dada para identificá-lo e, em seguida, insira o comentário como este: /^ *\[ *"$PS1 =/s/^/#/
  3. Use o comando a de sed para anexar uma determinada linha

No seu caso, você só quer adicionar algo à linha, então

sed 's/\( *\[ "$PS1" = "..s-..v...$ " \] && PS1="\[[email protected] \)\(.W]..$ "\)/#\
\D{%T} /' /etc/bashrc 
    
por 06.02.2018 / 14:11
0

Para fazer esse tipo de substituição de padrão complexo com sed:

sed 's/${pattern}/#${pattern}/'

você precisa forçar a avaliação de pattern dessa maneira:

sed 's/'${pattern}'/#'${pattern}'/'

Verifique se isso alcança exatamente o que você deseja no arquivo temporário antes de tentar tocar /etc/bashrc e usar sudo (Além disso, antes de usar sudo você deve verificar se está usando o caminho certo para evitar usar um falso um).

    
por 06.02.2018 / 15:38
0

Se você quiser substituir uma linha inteira sem regex, também poderá obter o número da linha primeiro:

LNO=$(grep -FN "$pattern" </etc/bashrc | cut -d':' -f1)

e substitua a linha por um padrão comentado e uma nova linha:

[ "$LNO" ] \
&& sed -i "${LNO}s/.*/#$pattern\n$newline/" /etc/bashrc

Este também é mais claro para ler meses depois.

Para fins de teste, eu sempre prefiro sed em um arquivo temporário primeiro (sem a opção -i), e renomeá-lo mais tarde para não danificar minha configuração.

Mas também descobri que esse método não funciona com padrões que contêm [,],$ and & ; os padrões também precisam de algum mascaramento nessa ordem:

pattern=${pattern//\[/\\[}
pattern=${pattern//\]/\]}
pattern=${pattern//\$/\$}
pattern=${pattern//\&/\&}
pattern=${pattern//\/\\}

que leva à seguinte função:

#!/bin/bash

declare -a commentAndReplaceMask=('[' ']' '$' '&')
function commentAndReplace () {
  local p="$1"  # pattern
  local s="$p" # sed pattern
  local r="$2"  # replacement
  local f="$3"  # file

  echo args:
  echo p: $p
  echo r: $r
  echo s: $s

  s=${s//\/\\}
  r=${r//\/\\}
  for m in ${commentAndReplaceMask[@]}; do
    s=${s//$m/\$m}
    r=${r//$m/\$m}
  done

  echo prepared for sed:
  echo p: $p
  echo r: $r
  echo s: $s

  LNO=$(grep -Fn "$p" <$f | cut -d':' -f1)

  [ "$LNO" ] \
  && sed "${LNO}s/.*/# $s\n$r/" <$f >${f}.tmp

  # mv "${f}.tmp" "$f"
}

commentAndReplace "$@"
    
por 06.02.2018 / 15:21