2011-05-10 10 views
27

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".¿Cómo uso sed para cambiar mis archivos de configuración, con claves flexibles y valores?

La disposición del archivo de configuración se parecería a este principio:

central.database = SQLFIRSTTEST 

Esto es lo que quiero que se vea como después de la sustitución sed:

central.database = SQLTEST 

que estoy haciendo esto de una bash script, cualquier sugerencia, recomendación o solución alternativa son bienvenidas!

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


Mi código actual (tercer intento): Mensaje

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 
) 

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 
+0

Encontré esto, ¿podría alguien explicar la sintaxis en la segunda publicación? http://ubuntuforums.org/showthread.php?t=708545 – prolink007

+0

Esperemos que alguien haya implementado una herramienta de este tipo en los últimos 5 años. –

+0

@ThomasWeller Su solicitud de recompensa es problemática. Está solicitando una solución no '' '' donde el título de la pregunta menciona 'sed'. La suya es básicamente una pregunta de recomendación de software, que está explícitamente prohibida en Stack Overflow. – tripleee

Respuesta

43

Aquí está una expresión de ejemplo:

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

Si desea hacer coincidir cosas con / en ella, puede utilizar otro delimitador:

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

O con la expansión de variables:

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

En su 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 está en válido. Además, sed no imprime su valor de retorno. Esta es la forma preferida para comprobar valores de retorno:

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

Y en última instancia la tubería para ssh (no probado):

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

El -i es para la sustitución de 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.

+0

¿Qué sucede si la cantidad de espacios entre "base de datos central" y el signo igual varía? ¿Y qué pasa si la cantidad de espacios varía entre el signo igual y el valor? ¿Y qué pasa si hay pestañas y espacios mezclados? – prolink007

+1

@ prolink007 Eso se explica. \ s * coincide con cualquier cantidad de caracteres en espacios en blanco (espacios, pestañas), 0 o más. En sed, creo \ s es igual a [\ t] – sapht

+0

¿Qué ocurre si quiero usar una variable bash para el "nuevo valor" en lugar del valor real? Por ejemplo, tengo una variable llamada "CENTRAL_DB_VALUE" que contiene una cadena del nuevo valor y quiero usar eso en el comando sed en lugar de la cadena actual. $ CENTRAL_DB_VALUE? – prolink007

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

Explicación:

  • -i le indica a sed para guardar 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 diagonales, es decir, "central.database =".
  • La parte s/OLD/NEW/ realiza un s ubstitution. La cadena VIEJA es una expresión regular para que coincida y la parte NEW es la cadena para sustituir en.
  • En expresiones regulares, .* significa "hacer coincidir cualquier cosa". Entonces, = .* coincide con un signo igual, espacio y luego con cualquier otra cosa después.
+11

+1 para la explicación clara – nico

+0

¿Qué sucede si la cantidad de espacios entre "base de datos central" y el signo igual varían. ¿Y qué pasa si la cantidad de espacios varía entre el signo igual y el valor? ¿Y qué pasa si hay tabulaciones y espacios mezclados? – prolink007

+0

¿Qué pasa si quiero usar una variable bash? para el "nuevo valor" en lugar del valor real?Por ejemplo, tengo una variable llamada "CENTRAL_DB_VALUE" que contiene una cadena del nuevo valor y quiero usar eso en el comando sed en lugar de la cadena actual. $ CENTRAL_DB_VALUE? – prolink007

2

Si desea reemplazar entre los 2 archivos de propiedades se puede utilizar este:

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 
4

me gusta usar awk para esto, ya que es bastante fácil de entender lo que está haciendo y cuida muy bien del separador (=) y también el hecho de que debe hacerse para una línea sin comentar:

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 

Este utiliza match() para comprobar si el patrón se produce 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 

Vamos a cambiar el valor en my_var-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 ajustar los valores de las 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 puede, por supuesto, poner todos los th está dentro de una función de shell que se 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 devuelve

hello 
my_var= NEW VALUE 
#my_var = ANOTHER VALUE 
bye 

Aunque también se puede hacer que el script recibir los parámetros de una manera como: ./script.sh "conf_file" "var_to_replace" "NEW VALUE" a continuación, pasarlos a la función.

+0

En un script de shell, ¿podría poner eso en una función para hacerlo aún más fácil? –

+0

@ThomasWeller por supuesto, vea mi respuesta actualizada con este enfoque también. – fedorqui

0

que utiliza este script para mantener las prioridades ..

Los argumentos $ 1 tendrá carpeta en la que existen múltiples archivos de configuración. $ 2 tendrá propiedades que necesitan ser reemplazados $ 1 ruta de acceso y caminos sub archivos # 3 tendrá propiedades que deben ser anulación en la parte superior de 2 $

Es lógica también se ha ocultado para comprobar la existencia de variables de entorno para el las claves existen en $ 2 y $ 3 y dan prioridad a eso.

i.e 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 <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

Esto funciona solo en entornos bash 4 –

0

sé que es demasiado tarde para añadir una respuesta a esta cuestión, sin embargo, pensé para compartir mis conocimientos a 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 su pregunta aquí está la respuesta

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

sed borra la línea cadena coincidente del archivo y la siguiente línea inmediata es la inserción de la llave requerida y el valor en el archivo.

Cuestiones relacionadas