usando sed con variables definidas en 2 matrices

usando sed con variables definidas en 2 matrices

He seguido muchas publicaciones en este sitio sobre el tema, pero claramente todavía estoy haciendo algo mal...

Mi objetivo es definir valores en dos matrices diferentes y luego usar sed para buscar una cadena de texto definida en la primera matriz con la de la segunda matriz.

Código a continuación:


#!/bin/bash

# define variables
defaultdirs=( Templates Documents Music Pictures Videos )
customdirs=( custom\/templates custom\/documents custom\/music custom\/pictures custom\/videos )

# replace text strings
for index in ${!defaultdirs[*]}
    do
    echo ${defaultdirs[$index]} will become ${customdirs[$index]}
    sed -i 's/${defaultdirs[$index]}/${customdirs[$index]}/g' ~/Desktop/scripts/test_replace.txt
done

echo genera las cadenas correctas, pero sed no obtiene la información correcta ya que el archivo de texto permanece sin cambios.

¿Pensamientos?

Como referencia, este es el contenido de test_replace.txt

# This file is written by xdg-user-dirs-update
# If you want to change or add directories, just edit the line you're
# interested in. All local changes will be retained on the next run.
# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an
# absolute path. No other format is supported.
# 
XDG_DESKTOP_DIR="$HOME/Desktop"
XDG_DOWNLOAD_DIR="$HOME/Downloads"
XDG_TEMPLATES_DIR="$HOME/Templates"
XDG_PUBLICSHARE_DIR="$HOME/Public"
XDG_DOCUMENTS_DIR="$HOME/Documents"
XDG_MUSIC_DIR="$HOME/Music"
XDG_PICTURES_DIR="$HOME/Pictures"
XDG_VIDEOS_DIR="$HOME/Videos"

Respuesta1

Primer problema: las cadenas entre comillas simples no tienen variables expandidas.

Segundo problema: tal como está, las barras en su reemplazo interrumpirán el s///gcomando después de solucionar el primer problema. Utilice un delimitador diferente para s.

Tercer problema (menor): está ejecutando sedvarias veces el mismo archivo, lo cual no es muy eficiente, y la -iopción de edición in situ no es estándar y las diferentes implementaciones que la proporcionan actúan de manera diferente (el caso común es que las personas El problema que tenemos es que la versión GNU no requiere un argumento, pero la versión Mac OS sí). Cuando desee editar un archivo y guardar los cambios, normalmente es mejor utilizar un editor de archivos dedicado como edo ex.

En su lugar , hacerlo todo con una sola edinvocación:

#!/bin/bash

# define variables
defaultdirs=(Templates Documents Music Pictures Videos)
customdirs=(custom/templates custom/documents custom/music custom/pictures custom/videos)

# replace text strings
(for index in ${!defaultdirs[*]}; do
     echo "${defaultdirs[$index]} will become ${customdirs[$index]}" >&2
     echo "g/${defaultdirs[$index]}/s|${defaultdirs[$index]}|${customdirs[$index]}|g"
 done;
 echo w) | ed -s test_replace.txt

Alternativa que envía los X will become Ymensajes a la salida estándar en lugar del error estándar y se ejecuta eden uncoproceso, con comandos individuales redirigidos a su entrada en lugar de utilizar una canalización:

#!/bin/bash

# define variables
defaultdirs=(Templates Documents Music Pictures Videos)
customdirs=(custom/templates custom/documents custom/music custom/pictures custom/videos)

coproc ED { ed -s test_replace.txt; } 2>/dev/null

# replace text strings
for index in ${!defaultdirs[*]}; do
     echo "${defaultdirs[$index]} will become ${customdirs[$index]}"
     echo "g/${defaultdirs[$index]}/s|${defaultdirs[$index]}|${customdirs[$index]}|g" >&${ED[1]}
done
printf '%s\n' w q >&${ED[1]}
wait $ED_PID

Respuesta2

También puedes recopilar todos los scomandos sustitutos en una sola entrada :sed

for index in ${!defaultdirs[*]}
  do   echo "s#${defaultdirs[$index]}#${customdirs[$index]}#g"
  done | sed -f- ~/Desktop/scripts/test_replace.txt

Respuesta3

La cuestión, comoShawn ya lo ha señalado, es que creas un sedscript con un error de sintaxis. El error de sintaxis proviene del hecho de que intentas utilizar /un scomando que utiliza ese mismo carácter como delimitador.

intentarpara contrarrestar esto escapando /en las cadenas de la customdirsmatriz, pero en realidad necesitaría \\/insertar un escape \en la cadena.

En cambio, aquí hay un enfoque diferente:

find_strings=( Templates Documents Music Pictures Videos )
replace_strings=( custom/templates custom/documents custom/music custom/pictures custom/videos )

set -- "${find_strings[@]}"

sed_stmts=( )
for replace_string in "${replace_strings[@]}"; do
   # sed_stmts+=( -e 's,\("$HOME/\)'"$1"'",\1'"$replace_string"'",' )

    # simpler, but less precise:
    #    sed_stmts+=( -e "s,$1,$replace_string," )
    # alternatively:
    #    sed_stmts+=( -e "s/${1//\//\\/}/${replace_string//\//\\/}/" )

    shift
done

sed "${sed_stmts[@]}" test_replace.txt >new-test_replace.txt

También me tomé la libertad de hacer las sustituciones un poco más confiables al hacer coincidir también la "$HOME/cadena de prefijo y el final ".

Esto terminará llamando sedasí:

sed -e 's,\("$HOME/\)Templates",\1custom/templates",' -e 's,\("$HOME/\)Documents",\1custom/documents",' -e 's,\("$HOME/\)Music",\1custom/music",' -e 's,\("$HOME/\)Pictures",\1custom/pictures",' -e 's,\("$HOME/\)Videos",\1custom/videos",' test_replace.txt

y lo hace creando un conjunto de seddeclaraciones en la matriz sed_stmtsque luego se usan en una sola llamada a sed.

El emparejamiento de los dos conjuntos de cadenas en las dos matrices se realiza asignando una de las matrices a la lista de parámetros posicionales usando sety luego iterando sobre la otra matriz. En cada iteración, shiftse utiliza para desplazar el elemento más frontal de la lista de parámetros posicionales.

información relacionada