Issue
I'd like to use reasoning similar to the approach below, which in this case uses a number (starting at 1) to replace the nth occurrence, but we want something in reverse order, that is, last, last 2nd, last 3rd, last 4th and so on.
QUESTION: Is it possible to use sed to substitute last, last 2nd, last 3rd, last 4th (and so on) match (occurrence)? If yes, how?
NOTE: Any approach that converts these numeric values to something specific to sed
(a bash function, for example) is also acceptable. Using sed
in conjunction with some command commonly found on Linux is also an acceptable answer. However, it is necessary that we can also use sed
with files by adding -i
.
EXAMPLE
sed -z 's/<TARGET>/<REPLACE>/<NTH_MATCH>'
EXAMPLE USE I
read -r -d '' FILE_CONTENT << 'HEREDOC'
BEGIN
askdjisoadjsadmsadjnasjndnsakjn
jkjkljkljklkljlkjkljl
jkljkljkljkklj
hjkhjkhkjh
jkjkljkljklkljlkjkljl
jkljkljkljkklj
hjkhjkhkjh
jdsjhfbjdhs
jdsjhfbjdhs
jdsjhfbjdhs
jdsjhfbjdhs
jdsjhfbjdhs
jdsjhfbjdhs
END
HEREDOC
SOURCE="${FILE_CONTENT:6:-3}"
echo "${SOURCE}" | sed -z 's/jkjkljkljklkljlkjkljl\njkljkljkljkklj\nhjkhjkhkjh/hbeuhyewuhdfjh\nfdsjisfdjiusdfijuhsfiuhsfdihusfdjhiusfdjkh\nkjhsfdjkfjkhsfdjknh/2'
EXAMPLE USE II
read -r -d '' FILE_CONTENT << 'HEREDOC'
BEGIN
askdjisoadjsadmsadjnasjndnsakjn
jkjkljkljklkljlkjkljl
jkljkljkljkklj
hjkhjkhkjh
jkjkljkljklkljlkjkljl
jkljkljkljkklj
hjkhjkhkjh
jdsjhfbjdhs
jdsjhfbjdhs
jdsjhfbjdhs
jdsjhfbjdhs
jdsjhfbjdhs
jdsjhfbjdhs
END
HEREDOC
SOURCE="${FILE_CONTENT:6:-3}"
echo "${SOURCE}" | sed -z 's/jdsjhfbjdhs/IReplacedTheLast4th/4'
**TIP:** This approach can be used to replace the last match (occurrence) -> `sed -z 's/\(.*\)<TARGET>/\1<REPLACE>/'`.
**Thanks!** 🤗
Solution
Basically you will need to reverse parameter using tac
and rev
. See the explanation in the code...
# SET PARAMETERS
## The "BEGIN"/"END" are didactic strategies to avoid the loss of whitespace at the end and beginning of the string
read -r -d '' SOURCE << 'HEREDOC'
BEGIN
askdjisoadjsadmsadjnasjndnsakjn
jkjkljkljklkljlkjkljl
jkljkljkljkklj
hjkhjkhkjh
jkjkljkljklkljlkjkljl
jkljkljkljkklj
hjkhjkhkjh
jdsjhfbjdhs
jdsjhfbjdhs
jdsjhfbjdhs
jdsjhfbjdhs
jdsjhfbjdhs
jdsjhfbjdhs
END
HEREDOC
SOURCE="${SOURCE:6:-3}"
TARGET="jdsjhfbjdhs"
REPLACE="IReplacedTheLast4th"
NTH_MATCH=4
# REVERSE PARAMETER CONTENT
TARGET=$(echo "x${TARGET}x" | tac | rev)
## Strategy to avoid losing whitespace at the end and beginning of the string
TARGET=${TARGET%?}
TARGET=${TARGET#?}
REPLACE=$(echo "x${REPLACE}x" | tac | rev)
REPLACE=${REPLACE%?}
REPLACE=${REPLACE#?}
SOURCE=$(echo "x${SOURCE}x" | tac | rev)
SOURCE=${SOURCE%?}
SOURCE=${SOURCE#?}
# USING STRING AS INPUT
echo "${SOURCE}" | sed -z 's/$TARGET/$REPLACE/$NTH_MATCH'
SED_RESULT=$(echo "x${SOURCE}x" | eval "sed -z 's/$TARGET/$REPLACE/$NTH_MATCH'")
## Undo reverse
SED_RESULT=$(echo "x${SED_RESULT}x" | tac | rev)
SED_RESULT=${SED_RESULT%?}
SED_RESULT=${SED_RESULT#?}
# USING FILE AS INPUT
echo "${SOURCE}" > ./my_file.txt
## Reverse file content input and undo on output to the same file
tac "./my_file.txt" | rev | eval "sed -z 's/$TARGET/$REPLACE/$NTH_MATCH'" | tac | rev > "./my_file.txt"
HOT: In this answer there is this implementation in a function with several interesting facilities for using with sed
.
Thanks! 😉
Answered By - Eduardo Lucio Answer Checked By - Marie Seifert (WPSolving Admin)