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///g
comando após corrigir o primeiro problema. Use um delimitador diferente para s
.
Terceiro (menor) problema: você está executando sed
várias vezes o mesmo arquivo, o que não é muito eficiente, e a -i
opçã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 ed
ou ex
.
Fazendo tudo com uma ed
invocaçã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 Y
mensagens para saída padrão em vez de erro padrão e é executada ed
em 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 s
comandos 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 sed
script com um erro de sintaxe. O erro de sintaxe vem do fato de você tentar usar /
um s
comando que usa esse mesmo caractere como delimitador.
Vocêtentarpara neutralizar isso escapando /
nas strings da customdirs
matriz, 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 sed
assim:
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 sed
instruções no array sed_stmts
que 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 set
e, 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.