Ich habe die zahlreichen Beiträge zu diesem Thema auf dieser Site verfolgt, aber ich mache offensichtlich immer noch etwas falsch …
Mein Ziel besteht darin, Werte in zwei unterschiedlichen Arrays zu definieren und dann mit sed eine im ersten Array definierte Textzeichenfolge mit der des zweiten Arrays zu durchsuchen.
Code unten:
#!/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 gibt die richtigen Zeichenfolgen aus, aber sed erhält nicht die richtigen Informationen, da die Textdatei unverändert bleibt.
Gedanken?
Als Referenz ist dies der Inhalt von 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"
Antwort1
Erstes Problem: In einfachen Anführungszeichen gesetzte Zeichenfolgen enthalten keine erweiterten Variablen.
Zweites Problem: So wie es ist, werden die Schrägstriche in Ihrem Ersatz den Befehl unterbrechen, s///g
nachdem das erste Problem behoben wurde. Verwenden Sie ein anderes Trennzeichen für s
.
Drittes (kleineres) Problem: Sie führen die Bearbeitung sed
mehrmals über dieselbe Datei aus, was nicht sehr effizient ist. Außerdem -i
ist die Option zur Bearbeitung direkt vor Ort nicht standardmäßig und verschiedene Implementierungen, die diese Option bieten, reagieren unterschiedlich (das häufigste Problem ist, dass die GNU-Version kein Argument erfordert, die Mac OS-Version jedoch schon). Wenn Sie eine Datei bearbeiten und Änderungen speichern möchten, ist es normalerweise besser, einen dedizierten Dateieditor wie ed
oder zu verwenden ex
.
Stattdessen alles mit einem ed
Aufruf erledigen:
#!/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
Alternative, die die X will become Y
Nachrichten an die Standardausgabe statt an die Standardfehlerausgabe sendet und ed
in einemCoprozess, wobei einzelne Befehle an die Eingabe umgeleitet werden, anstatt eine Pipeline zu verwenden:
#!/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
Antwort2
Sie können alle Ersatzbefehle auch s
in einer Eingabe sammeln :sed
for index in ${!defaultdirs[*]}
do echo "s#${defaultdirs[$index]}#${customdirs[$index]}#g"
done | sed -f- ~/Desktop/scripts/test_replace.txt
Antwort3
Das Problem, wieShawn hat bereits darauf hingewiesen, ist, dass Sie ein Skript mit einem Syntaxfehler erstellen sed
. Der Syntaxfehler entsteht dadurch, dass Sie versuchen, /
einen s
Befehl zu verwenden, der dasselbe Zeichen als Trennzeichen verwendet.
Duversuchenum dem entgegenzuwirken, indem Sie die /
Zeichenfolgen im customdirs
Array maskieren, aber Sie müssten tatsächlich eine Maskierung in die Zeichenfolge \\/
einfügen .\
Stattdessen gibt es hier einen anderen Ansatz:
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
Ich habe mir außerdem die Freiheit genommen, die Ersetzungen etwas zuverlässiger zu machen, indem ich auch die "$HOME/
Präfixzeichenfolge und das Finale abgeglichen habe "
.
sed
Der Aufruf sieht dann folgendermaßen aus :
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
Dies geschieht durch den Aufbau einer Reihe von sed
Anweisungen im Array sed_stmts
, die dann in einem einzigen Aufruf von verwendet werden sed
.
Die Paarung der beiden String-Sets in den beiden Arrays erfolgt, indem eines der Arrays mit der Liste der Positionsparameter zugewiesen wird set
und dann über das andere Array iteriert wird. In jeder Iteration shift
wird verwendet, um das vorderste Element der Liste der Positionsparameter zu verschieben.