¿Cómo uso sed para cambiar mis archivos de configuración, con claves flexibles y valores?

Quiero buscar un archivo de configuración para esta expresión: “central.database”. Luego quiero cambiar la configuración asociada con “central.database” a “SQLTEST”.

El diseño del archivo de configuración se vería así inicialmente:

central.database = SQLFIRSTTEST 

Esto es lo que quiero que se vea después del reemplazo sed:

 central.database = SQLTEST 

Estoy haciendo esto en un script bash, ¡cualquier sugerencia, recomendación o solución alternativa es bienvenida!

(En realidad, tanto central.database como SQLTEST provienen de variables de bash aquí).


Mi código actual (tercer bash):

 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 ) 

Mensaje de error:

 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 

Aquí hay una expresión de ejemplo:

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

Si quieres unir cosas con / en él, puedes usar otro delimitador:

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

O con expansión variable:

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

En tu ejemplo:

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

Hay un \ 1 antes de $ CENTRAL_DB_NAME que no es válido. Además, sed no imprime su valor de retorno. Esta es la forma preferida de verificar los valores devueltos:

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

Y, finalmente, canalizar a ssh (no probado):

 sed_return_value=$(ssh server < 

El -i es para reemplazar datos en el archivo de entrada. De lo contrario, sed escribe en stdout.

Las expresiones regulares son un campo propio. Sería imposible explicarlos en profundidad en una respuesta de stackoverflow, a menos que haya alguna función específica que te elude.

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

Explicación:

  • -i le dice a sed que guarde los resultados en el archivo de entrada. Sin él sed imprimirá los resultados a stdout.
  • /central.database =/ coincide con las líneas que contienen la cadena entre barras, es decir, “central.database =”.
  • La parte s/OLD/NEW/ realiza una sustitución. La secuencia VIEJA es una expresión regular para unir y la parte NEW es la cadena para sustituirla.
  • En expresiones regulares,. .* Significa “hacer coincidir cualquier cosa”. Así que = .* Coincide con un signo igual, espacio y luego cualquier otra cosa después.

Me gusta usar awk para esto, ya que es bastante fácil entender lo que está haciendo y se ocupa muy bien del separador ( = ) y también del hecho de que debe hacerse en una línea no comentada:

 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 

Esto usa match() para verificar si el patrón ocurre en cualquier línea dada. Si lo hace, realiza el reemplazo con el valor dado.

Por ejemplo:

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

Cambiemos el valor en my_var a 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 

También es posible establecer los valores en variables de shell y luego usarlos con -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 

Y, por supuesto, puede poner todo esto dentro de una función de shell que luego llama normalmente:

 #!/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" 

Tras la ejecución, esto vuelve

 hello my_var= NEW VALUE #my_var = ANOTHER VALUE bye 

Si bien también puede hacer que el script reciba los parámetros de la siguiente manera: ./script.sh "conf_file" "var_to_replace" "NEW VALUE" para pasarlos a la función.

Si desea reemplazar entre 2 archivos de propiedades, puede usar esto:

 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 

Sé que es demasiado tarde para agregar una respuesta a esta pregunta, sin embargo, pensé en compartir mi conocimiento con todos ustedes. Hay un enfoque muy general que he seguido para resolver un tipo similar de problema. He eliminado toda la línea que corresponde a la cadena y he añadido los valores necesarios a esa clave. A tu pregunta aquí está la respuesta

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

sed elimina la línea de cadena correspondiente del archivo y la siguiente línea inmediata inserta la clave y el valor requeridos en el archivo.

Usé este script para mantener las prioridades.

Los argumentos $ 1 tendrán una carpeta en la que existen varios archivos de configuración. $ 2 tendrán propiedades que deben ser reemplazadas en $ 1 ruta y sub paths archivos # 3 tendrán propiedades que necesitan ser reemplazadas por encima de $ 2

También tiene lógica oculta para verificar la existencia de variables de entorno para las claves existentes en $ 2 y $ 3 y dar prioridad a eso.

es decir, si existe una clave en el entorno que sería la más alta prioridad. Al lado de eso sería $ 3 y al lado de eso sería $ 1 archivo.

 #!/bin/bash #Usage is propertyReplacer    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 }