首页 > 解决方案 > 当我尝试仅匹配脚本中的最后一次出现时,Sed 不遵守 $ 地址字符

问题描述

**注意:我在堆栈交换上也问过这个问题,因为我在编辑这篇文章时遇到了问题。幸运的是,我在那里得到了答案:

https://unix.stackexchange.com/questions/546208/sed-in-not-honoring-the-address-char-when-i-try-to-only-match-last-occurrence?noredirect=1#comment1013405_546208

^^^ 请参阅上面的链接以获取我发现对我有用的解决方案^^^

我创建了一个简单的脚本,我想用它来根据模式重命名文件。

该脚本使用 find 和 sed 来完成它的工作,

一切基本上都在工作,除了当我使用 $ 告诉 sed 只匹配最后一次出现时,它无法匹配任何东西。

如果我删除 $ 所有匹配项,这样就可以了,但我特别想只针对最后一次出现。示例: sed "s/${search}/${replace}/g" # 查找匹配项 sed "s/${search}$/${replace}/g" # 未找到匹配项

我已经简化了下面的代码,searchReplaceLastMatchOnly() 函数演示了我试图用 sed 做什么,但它不起作用。

searchReplace() 函数显示如果不使用 $ 地址,sed 模式如何查找匹配项。

我的测试目录如下: ./bar ./bar/baz ./bar/foobar.txt ./bar/baz/foobar.txt

# Search and replace using command: sed "s/${search}/${replace}/g")"
# This works, but is not what i want, i just need last occurence.
function searchReplace() {
    # spaces are set as defaults
    target=${1:- }
    search=${2:- }
    replace=${3:- }
    result="$(printf "%s" "$target" | sed "s/${search}/${replace}/g")"
    printf "%s" "$result"
}

# Search and replace using command: sed "s/${search}$/${replace}/g")"
# This is what I have read should work, using $ should only match
# last occurence, this is what I want but it does not work.
function searchReplaceLastMatchOnly() {
    # spaces are set as defaults
    target=${1:- }
    search=${2:- }
    replace=${3:- }
    ### sed NOT WORKING WITH $ ###
    result="$(printf "%s" "$target" | sed "s/${search}$/${replace}/g")"
    printf "%s" "$result"
}

# demo
search="bar"
replace="Bar"
dirPath="."
pattern="*.txt"
printf "\n"
find "$dirPath" -type f -name "$pattern" | while IFS= read -r original; do
    modified="$(searchReplaceLastMatchOnly "$original" "$search" "$replace")"
    printf "\nRenaming %s to %s\n" "$original" "$modified"
    #mv "$original" "$modified"
done
printf "\n"

# Currently this has following results:
#
# Renaming ./bar/baz/foobar.txt to ./bar/baz/foobar.txt
#
# Renaming ./bar/foobar.txt to ./bar/foobar.txt
#

printf "\n"
find "$dirPath" -type f -name "$pattern" | while IFS= read -r original; do
    modified="$(searchReplace "$original" "$search" "$replace")"
    printf "\nRenaming %s to %s\n" "$original" "$modified"
    #mv "$original" "$modified"
done
printf "\n"

# Currently this has following results:
#
# Renaming ./bar/baz/foobar.txt to ./Bar/baz/fooBar.txt
#
# Renaming ./bar/foobar.txt to ./Bar/fooBar.txt

标签: linuxbashshellunix

解决方案


不是答案,而是让您深入了解(GNU)sed标志。

gflag 用于全局,替换所有匹配项。

$ echo barbarbar.etc | sed 's/bar/foo/g'
foofoofoo.etc

$ echo barbarbar.etc | sed 's/bar/foo/'
foobarbar.etc        

$ echo barbarbar.etc | sed 's/bar/foo/1'
foobarbar.etc

$ echo barbarbar.etc | sed 's/bar/foo/2'
barfoobar.etc

$ echo barbarbar.etc | sed 's/bar/foo/3'
barbarfoo.etc

$ echo barbarbar.etc | sed -E 's/bar(.etc)$/foo\1/'
barbarfoo.etc

$这是行尾的锚点,上面的输入不需要,但对于另一个输入,它只会匹配行尾的实例..

$ echo barbarbar.etc bar.etc| sed -E 's/bar(.etc)$/foo\1/'
barbarbar.etc foo.etc

推荐阅读