Вам нужно экранировать (обратной косой чертой\
)все замененные косые черты /
и все обратные косые черты \
отдельно, поэтому:
$ echo "/tmp/test/folder1/test.txt" | sed 's/\//\\\//g'
\/tmp\/test\/folder1\/test.txt
но это довольно нечитабельно.
Однако sed
позволяет использовать в качестве разделителя практически любой символ вместо /
, это особенно полезно, когда нужно заменить косую черту /
, как в вашем случае, например, используя точку с запятой ;
в качестве разделителя команда стала бы проще:
echo "/tmp/test/folder1/test.txt" | sed 's;/;\\/;g'
Другие случаи:
Если кто-то хочет придерживаться косой черты в качестве разделителя и использовать двойные кавычки, тогда все экранированные обратные косые черты должны быть экранированы еще раз, чтобы сохранить их буквальные значения:
echo "/tmp/test/folder1/test.txt" | sed "s/\//\\\\\//g"
если вообще не нужны кавычки, то нужна еще одна обратная косая черта:
echo "/tmp/test/folder1/test.txt" | sed s/\\//\\\\\\//g
Анкер $
:
Matches the end of a string without consuming any characters. If multiline mode is used, this will also match immediately before a newline character.
Это отличается от "последнего появления", так как ваши строки на самом деле заканчиваются на txt$
, а не на bar$
и не совпадают.
Один из способов сопоставить только последнее вхождение — использовать rev
для реверсирования вашей строки и замены только первого вхождения (, конечно, ваши замены также должны быть реверсированы!)
function searchReplaceLastMatchOnly() {
# spaces are set as defaults
local target=$(rev <<<"${1:- }")
local search=$(rev <<<"${2:- }")
local replace=$(rev <<<"${3:- }")
local esearch=$(printf '%s\n' "$search" | sed 's:[][\/.^$*]:\\&:g')
local ereplace=$(printf '%s\n' "$replace" | sed 's:[\/&]:\\&:g;$!s/$/\\/')
result="$(printf "%s" "$target" | sed "s/${esearch}/${ereplace}/" | rev)"
printf "%s" "$result"
}
Обратите внимание, :оператор g
был удален из команды sed
, поэтому заменяется только первое вхождение . Кроме того, мы экранируем переменные , которые будут переданы в sed
, чтобы предотвратить их нежелательную интерпретацию.
Чтобы заменить только последнее совпадение, замените
result="$(printf "%s" "$target" | sed "s/${search}$/${replace}/g")
с
result="$(printf "%s" "$target" | sed "s/\(.*\)${search}/\1${replace}/")"
$
соответствует концу строки, а не последнему шаблону поиска, и вам не нужен модификатор g
, так как существует (не более )одной замены.
\(.*\)
является жадным и sed
сопоставляет все до того, как найдет последний шаблон $search
. И поскольку мы не хотим удалять эту часть, мы должны включить ее как \1
в замену.
Примечания:
#!/bin/bash
в первой строке. -e
еще не ()реализовано. Существует множество переменных, которые используются только один раз. Их удаление сделает ваш код намного чище, например. функция searchReplaceLastMatchOnly()
может быть сведена к
function searchReplaceLastMatchOnly() {
printf "%s" "${1:- }" | sed "s/\(.*\)${2:- }/\1${3:- }/"
}