usando sed com variáveis ​​definidas em 2 arrays

usando sed com variáveis ​​definidas em 2 arrays

Tenho acompanhado muitas postagens neste site sobre o assunto, mas ainda estou claramente fazendo algo errado...

Meu objetivo é definir valores em dois arrays diferentes e, em seguida, usar sed para pesquisar uma string de texto definida no primeiro array com a do segundo array.

Código abaixo:


#!/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 gera as strings corretas, mas sed não está obtendo as informações corretas, pois o arquivo de texto permanece inalterado.

Pensamentos?

Para referência, este é o conteúdo 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"

Responder1

Primeiro problema: strings entre aspas simples não possuem variáveis ​​expandidas.

Segundo problema: no estado em que se encontra, as barras em sua substituição interromperão o s///gcomando após corrigir o primeiro problema. Use um delimitador diferente para s.

Terceiro (menor) problema: você está executando sedvárias vezes o mesmo arquivo, o que não é muito eficiente, e a -iopção de edição no local não é padrão e diferentes implementações que fornecem isso agem de maneira diferente (o caso comum de pessoas O problema é que a versão GNU não requer um argumento, mas a versão Mac OS exige). Ao querer editar um arquivo e salvar as alterações, geralmente é melhor usar um editor de arquivos dedicado como edou ex.

Fazendo tudo com uma edinvocação:

#!/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 envia as X will become Ymensagens para saída padrão em vez de erro padrão e é executada edem umcoprocessar, com comandos individuais redirecionados para sua entrada em vez de usar um pipeline:

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

Responder2

Você também pode coletar todos os scomandos substitutos em uma entrada para :sed

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

Responder3

A questão, comoShawn já apontou, é que você cria um sedscript com um erro de sintaxe. O erro de sintaxe vem do fato de você tentar usar /um scomando que usa esse mesmo caractere como delimitador.

Vocêtentarpara neutralizar isso escapando /nas strings da customdirsmatriz, mas você precisaria \\/realmente inserir um escape \na string.

Em vez disso, aqui está uma abordagem 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

Também tomei a liberdade de tornar as substituições um pouco mais confiáveis, combinando também a "$HOME/string do prefixo e o final ".

Isso acabará chamando sedassim:

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

e faz isso criando um conjunto de sedinstruções no array sed_stmtsque são usadas em uma única chamada para sed.

O emparelhamento dos dois conjuntos de strings nas duas matrizes é feito atribuindo uma das matrizes à lista de parâmetros posicionais usando sete, em seguida, iterando sobre a outra matriz. Em cada iteração, shifté usado para deslocar o elemento mais à frente da lista de parâmetros posicionais.

informação relacionada