web-dev-qa-db-fra.com

Comment utiliser sed pour modifier mes fichiers de configuration, avec des clés et des valeurs flexibles?

Je souhaite rechercher dans un fichier de configuration cette expression "central.database" . Je souhaite ensuite modifier le paramètre associé à "central.database" en "SQLTEST".

La disposition du fichier de configuration ressemblerait à ceci:

central.database = SQLFIRSTTEST

Voici ce que je veux qu'il ressemble après le remplacement de sed:

central.database = SQLTEST

Je le fais dans un script bash, toute suggestion, recommandation ou solution alternative est la bienvenue!

(En réalité, central.database et SQLTEST proviennent des variables bash ici.)


Mon code actuel (troisième tentative):

sshRetValue=$(ssh -p "35903" -i $HOME/sshids/idrsa-1.old ${1} <<EOF
        sed -i "s/^\($CENTRAL_DB_NAME\s*=\s*\).*\$/\1$CENTRAL_DB_VALUE/" /home/testing.txt;
        echo $?
EOF
)

Message d'erreur:

Pseudo-terminal will not be allocated because stdin is not a terminal.
sed: -e expression #1, char 58: unknown option to `s'
-bash: line 3: EOF: command not found
34
prolink007

Voici un exemple d'expression:

sed -i 's/^\(central\.database\s*=\s*\).*$/\1SQLTEST/' file.cfg

Si vous voulez faire correspondre les éléments avec /, vous pouvez utiliser un autre délimiteur:

sed -i 's#^\(cent/ral\.data/base\s*=\s*\).*$#\1SQL/TEST#' file.cfg

Ou avec expansion variable:

VAL="SQLTEST"
sed -i "s/^\(central\.database\s*=\s*\).*\$/\1$VAL/" file.cfg

Dans votre exemple: 

sshRetValue=`sed -i "s/^\(\1$CENTRAL_DB_NAME\s*=\s*\).*\$/\1$CENTRAL_DB_VALUE/" /home/testing.txt`;

Il y a un\1 avant $ CENTRAL_DB_NAME qui n'est pas valide. De plus, sed n'imprime pas sa valeur de retour. C'est le moyen préféré pour vérifier les valeurs de retour:

sed -i "s/^\($CENTRAL_DB_NAME\s*=\s*\).*\$/\1$CENTRAL_DB_VALUE/" /home/testing.txt;
sed_return_value=$?

Et finalement, la tuyauterie vers SSH (non testée):

sed_return_value=$(ssh server <<EOF
    sed -i "s/^\($CENTRAL_DB_NAME\s*=\s*\).*\$/\1$CENTRAL_DB_VALUE/" /home/testing.txt;
    echo $?
EOF
)

Le -i sert à remplacer des données dans le fichier d'entrée. Sinon, sed écrit sur stdout.

Les expressions régulières sont un champ qui leur est propre. Il serait impossible de les expliquer en profondeur dans une réponse stackoverflow, à moins qu'une fonction spécifique ne vous échappe.

53
sapht
sed -i -e '/central.database =/ s/= .*/= new_value/' /path/to/file

Explication:

  • -i indique à sed de sauvegarder les résultats dans le fichier d'entrée. Sans cela, sed imprimera les résultats sur la sortie standard.
  • /central.database =/ fait correspondre les lignes contenant la chaîne entre des barres obliques, c’est-à-dire "central.database =".
  • La partie s/OLD/NEW/ effectue une substitution. La chaîne OLD est une expression régulière à associer et la partie NEW est la chaîne à remplacer.
  • Dans les expressions régulières, .* signifie "correspond à rien". Donc, = .* correspond à un signe égal, à un espace, puis à tout le reste.
79
John Kugelman

Je sais qu'il est trop tard pour ajouter une réponse à cette question, cependant, j'ai pensé partager mes connaissances avec vous tous. J'ai suivi une approche très générale pour résoudre un problème similaire. J'ai supprimé la ligne entière qui correspond à la chaîne et ajouté les valeurs requises à cette clé. A votre question voici la réponse

replaceValue=SQLTEST
sed -i "/central.database =/d" /home/testing.txt
echo "central.database = $replaceValue"  >> /home/testing.txt

sed supprime la chaîne correspondante du fichier et la ligne immédiatement suivante insère la clé et la valeur requises dans le fichier.

7
Alex Raj Kaliamoorthy

J'aime utiliser awk pour cela, car il est assez facile de comprendre ce qu'il fait et prend très bien soin du séparateur (=) et du fait qu'il doit être fait sur une ligne non commentée:

awk -v var="my_var" -v new_val="NEW VALUE" \  # set the vars
    'BEGIN{FS=OFS="="}                        # set separator to =
     match($1, "^\\s*" var "\\s*") {          # check if it matches
         $2=" " new_val                       # if so, replace the line
     }1' conf_file                            # print all lines

Ceci utilise match() pour vérifier si le motif se produit dans une ligne donnée. Si c'est le cas, il effectue le remplacement avec la valeur donnée.

Par exemple:

$ cat conf
hello
my_var= SOME VALUE
#my_var = ANOTHER VALUE
bye

Modifions la valeur dans my_var en NEW VALUE:

$ awk -v var="my_var" -v new_val="NEW VALUE" 'BEGIN{FS=OFS="="}match($1, "^\\s*" var "\\s*") {$2=" " new_val}1' conf
hello
my_var= NEW VALUE
#my_var = ANOTHER VALUE
bye

Il est également possible de définir les valeurs dans les variables Shell, puis de les utiliser avec -v:

$ var="my_var"
$ new_value="NEW VALUE"
$ awk -v var="$var" -v new_val="$new_value" 'BEGIN{FS=OFS="="}match($1, "^\\s*" var "\\s*") {$2=" " new_val}1' conf

Et vous pouvez bien sûr mettre tout cela dans une fonction Shell que vous appelez ensuite normalement:

#!/bin/bash

replace () {
   file=$1
   var=$2
   new_value=$3
   awk -v var="$var" -v new_val="$new_value" 'BEGIN{FS=OFS="="}match($1, "^\\s*" var "\\s*") {$2=" " new_val}1' "$file"
}

# Call the replace() function with the necessary parameters
replace "conf" "my_var" "NEW VALUE" 

Lors de l'exécution, cela retourne

hello
my_var= NEW VALUE
#my_var = ANOTHER VALUE
bye

Vous pouvez également faire en sorte que le script reçoive les paramètres de la manière suivante: ./script.sh "conf_file" "var_to_replace" "NEW VALUE" pour les transmettre ensuite à la fonction.

4
fedorqui

Si vous souhaitez remplacer entre 2 fichiers de propriétés, vous pouvez utiliser ceci: 

awk -F= 'NR==FNR{A[$1]=$2;next}$1 in A{$2=A[$1]}1' OFS='\=' /tmp/masterfile /opt/props/finalfile.properties > /tmp/tmp.txt && mv -f /tmp/tmp.txt /opt/props/finalfile.properties
2
user5433596

J'ai utilisé ce script pour garder les priorités ..

Les arguments $ 1 aura un dossier dans lequel plusieurs fichiers de configuration existent. $ 2 aura des propriétés qui doivent être remplacées dans les fichiers chemin et sous-chemins $ 1 # 3 aura des propriétés qui doivent être écrasées sur $ 2

Il a également une logique cachée pour vérifier l'existence de variables d'environnement car les clés existent dans $ 2 et $ 3 et donnent la priorité à cela.

c'est-à-dire s'il existe une clé dans un environnement qui aurait la plus haute priorité. À côté de cela, 3 $ et à côté de cela, 1 $ de fichier.

#!/bin/bash
#Usage is propertyReplacer <CONFIG_FOLDER_PATH> <CONFIG_FILE_2ND_PRIORITY> <CONFIG_FILE_1ST_PRIORITY>
function propertyReplacer() {

  filePathToAct="$1"
  propertiesFilePath="$2"
  propertiesSecureFilePath="$3"

  declare -A keyValues

  while IFS='=' read -r key value; do
    if [  "$key" == "" ]; then
      continue
    Elif [[  "$key" =~ ^#.*$ ]]; then
      continue
    else
      echo $key " --> " $value
      keyValues[$key]=$value
    fi
  done < "$propertiesFilePath"

  if [ ! -f "$propertiesSecureFilePath" ]; then
    continue
  else
    while IFS='=' read -r key value; do
      if [  "$key" == "" ]; then
        continue
      Elif [[  "$key" =~ ^#.*$ ]]; then
        continue
      else
        echo $key " --> " $value
        keyValues[$key]=$value
      fi
    done < "$propertiesSecureFilePath"
  fi

  for key in ${!keyValues[@]}; do
    envProp=${key//[@]/}
    if [  "$(eval echo '$'$envProp)" == "" ]; then
      echo "Environment key not exist" $envProp
    else
      value=$(eval echo '$'$envProp)
      echo "From Environment " $envProp " --> "$value 
      keyValues[$key]=$value
    fi
  done 


find "$filePathToAct" | while read -r resultFileName; do
  if [ ! -f "$resultFileName" ]; then
    continue
  else
    echo "Acting on the file $resultFileName"

    for key in ${!keyValues[@]}; do
      value=$(echo "${keyValues[${key}]}" | sed 's/\//\\\//g')
      echo "sed -i 's/$key/$value/g' $resultFileName "
      eval "sed -i 's/$key/$value/g' $resultFileName "
    done 
  fi
done 

} 
0
Venkateswara Rao