использование sed с переменными, определенными в 2 массивах

использование sed с переменными, определенными в 2 массивах

Я следил за многими сообщениями на этом сайте по этой теме, но я все еще явно делаю что-то не так...

Моя цель — определить значения в двух разных массивах, а затем использовать 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используется для сдвига самого переднего элемента списка позиционных параметров.

Связанный контент