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///g
comando después de solucionar el primer problema. Utilice un delimitador diferente para s
.
Tercer problema (menor): está ejecutando sed
varias veces el mismo archivo, lo cual no es muy eficiente, y la -i
opció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 ed
o ex
.
En su lugar , hacerlo todo con una sola ed
invocació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 Y
mensajes a la salida estándar en lugar del error estándar y se ejecuta ed
en 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 s
comandos 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 sed
script con un error de sintaxis. El error de sintaxis proviene del hecho de que intentas utilizar /
un s
comando que utiliza ese mismo carácter como delimitador.
Túintentarpara contrarrestar esto escapando /
en las cadenas de la customdirs
matriz, 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 sed
así:
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 sed
declaraciones en la matriz sed_stmts
que 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 set
y luego iterando sobre la otra matriz. En cada iteración, shift
se utiliza para desplazar el elemento más frontal de la lista de parámetros posicionales.