Я следил за многими сообщениями на этом сайте по этой теме, но я все еще явно делаю что-то не так...
Моя цель — определить значения в двух разных массивах, а затем использовать sed для поиска строки текста, определенной в первом массиве, с помощью строки текста во втором массиве.
Код ниже:
#!/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 выводит правильные строки, но sed не получает нужную информацию, поскольку текстовый файл остается неизменным.
Мысли?
Для справки, вот содержимое 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"
решение1
Первая проблема: строки в одинарных кавычках не содержат развернутых переменных.
Вторая проблема: Как есть, слеши в вашей замене сломают s///g
команду после исправления первой проблемы. Используйте другой разделитель для s
.
Третья (меньшая) проблема: вы запускаете sed
несколько раз один и тот же файл, что не очень эффективно, а -i
опция редактирования на месте нестандартна, и разные реализации, которые ее предоставляют, действуют по-разному (Обычный случай, с которым сталкиваются люди, заключается в том, что версия GNU не требует аргумента, а версия Mac OS требует). Когда вы хотите отредактировать файл и сохранить изменения, обычно лучше использовать специальный редактор файлов, например ed
или ex
.
Вместо этого сделаем все это одним ed
вызовом:
#!/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
Альтернатива, которая отправляет X will become Y
сообщения в стандартный вывод вместо стандартной ошибки и работает ed
всовместный процесс, при этом отдельные команды перенаправляются на его вход вместо использования конвейера:
#!/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
решение2
Вы также можете собрать все s
заменяющие команды в один вход :sed
for index in ${!defaultdirs[*]}
do echo "s#${defaultdirs[$index]}#${customdirs[$index]}#g"
done | sed -f- ~/Desktop/scripts/test_replace.txt
решение3
Проблема, какШон уже указал, заключается в том, что вы создаете sed
скрипт с синтаксической ошибкой. Синтаксическая ошибка возникает из-за того, что вы пытаетесь использовать /
в s
команде, которая использует тот же символ в качестве разделителя.
Тыпытатьсячтобы противодействовать этому, экранируя /
строки в customdirs
массиве, но вам придется \\/
фактически вставить экранирование \
в строку.
Вместо этого предлагаем другой подход:
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
Я также позволил себе сделать замены немного более надежными, сопоставив также "$HOME/
префиксную строку и конечный символ "
.
В конечном итоге вызов будет выглядеть sed
так:
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
и он делает это путем создания набора sed
операторов в массиве sed_stmts
, которые затем используются в одном вызове sed
.
Объединение двух наборов строк в двух массивах выполняется путем назначения одного из массивов списку позиционных параметров с помощью set
, а затем итерации по другому массиву. В каждой итерации shift
используется для сдвига самого переднего элемента списка позиционных параметров.