2つの配列で定義された変数でsedを使用する

2つの配列で定義された変数でsedを使用する

私はこの件に関してこのサイトで非常に多くの投稿を読んできましたが、それでも明らかに何か間違ったことをしています...

私の目標は、2 つの異なる配列に値を定義し、sed を使用して、最初の配列で定義されたテキスト文字列を 2 番目の配列の文字列で検索することです。

以下のコード:


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

最初の問題: 一重引用符で囲まれた文字列には展開された変数がありません。

2 番目の問題: 現状では、s///g最初の問題を修正した後、置換後のスラッシュによってコマンドが機能しなくなります。 に別の区切り文字を使用してくださいs

sed3 つ目の (より小さな) 問題:同じファイルを複数回実行しているため、あまり効率的-iではありません。また、インプレース編集のオプションは標準ではなく、それを提供するさまざまな実装は異なる動作をします (よくある問題として、GNU バージョンでは引数が必要ないのに、Mac OS バージョンでは必要であるという点があります)。ファイルを編集して変更を保存する場合は、通常、 や などの専用のファイル エディターを使用する方がよいでしょedex

代わりに1 回の呼び出しですべてを実行します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すべての代替コマンドを 1 つの入力に集めることもできます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、それを 1 回の呼び出しで使用することによって行われますsed

2 つの配列内の 2 セットの文字列をペアにするには、 を使用して配列の 1 つを位置パラメータのリストに割り当てset、次に他の配列を反復処理します。各反復処理では、を使用してshift位置パラメータのリストの最前面の要素をシフトします。

関連情報