假设我有'abbc'字符串,我想替换:

Ab -> BC BC -> ab

如果我尝试两次替换,结果不是我想要的:

echo 'abbc' | sed 's/ab/bc/g;s/bc/ab/g'
abab

那么我可以像下面这样使用什么sed命令来替换?

echo abbc | sed SED_COMMAND
bcab

编辑: 实际上,文本可以有超过2个模式,我不知道我需要多少替换。因为有一个答案说sed是一个流编辑器,它的替换是贪婪的,我认为我将需要使用一些脚本语言。


当前回答

用户“C: \ \圣回响。谭\我的文件夹\ project1”| sed - e ' s / C: \ \ / mnt \ / C \ / /, / \ \ / \ / / g’

替换

C:\Users\San.谭\我的文件夹\ project1

to

mnt / c /用户/圣。棕褐色/我的文件夹/ project1

以防有人需要将windows路径替换为windows子系统for Linux(WSL)路径

其他回答

这是一个基于oogas sed的awk

echo 'abbc' | awk '{gsub(/ab/,"xy");gsub(/bc/,"ab");gsub(/xy/,"bc")}1'
bcab

这可能为你工作(GNU sed):

sed -r '1{x;s/^/:abbc:bcab/;x};G;s/^/\n/;:a;/\n\n/{P;d};s/\n(ab|bc)(.*\n.*:(\1)([^:]*))/\4\n\2/;ta;s/\n(.)/\1\n/;ta' file

它使用一个查找表,该表准备好并保存在保留空间(HS)中,然后附加到每一行。一个唯一的标记(在本例中是\n)被前置在行开始之前,并用作在整个行长度上进行搜索的方法。一旦标记到达行尾,该过程就完成了,并打印出查找表,标记被丢弃。

注意:查找表在一开始就准备好了,并且选择了第二个唯一标记(在本例中为:),以避免与替换字符串冲突。

以下是一些评论:

sed -r '
  # initialize hold with :abbc:bcab
  1 {
    x
    s/^/:abbc:bcab/
    x
  }

  G        # append hold to patt (after a \n)

  s/^/\n/  # prepend a \n

  :a

  /\n\n/ {
    P      # print patt up to first \n
    d      # delete patt & start next cycle
  }

  s/\n(ab|bc)(.*\n.*:(\1)([^:]*))/\4\n\2/
  ta       # goto a if sub occurred

  s/\n(.)/\1\n/  # move one char past the first \n
  ta       # goto a if sub occurred
'

这个表格是这样的:

   **   **   replacement
:abbc:bcab
 **   **     pattern

Tcl有一个内置的功能

$ tclsh
% string map {ab bc bc ab} abbc
bcab

它的工作原理是每次遍历字符串的一个字符,从当前位置开始进行字符串比较。

在perl中:

perl -E '
    sub string_map {
        my ($str, %map) = @_;
        my $i = 0;
        while ($i < length $str) {
          KEYS:
            for my $key (keys %map) {
                if (substr($str, $i, length $key) eq $key) {
                    substr($str, $i, length $key) = $map{$key};
                    $i += length($map{$key}) - 1;
                    last KEYS;
                }
            }
            $i++;
        }
        return $str;
    }
    say string_map("abbc", "ab"=>"bc", "bc"=>"ab");
'
bcab

到目前为止发布的每个答案似乎都同意kuriouscoder在上面帖子中的说法:

达到你要求的唯一方法就是使用中间产物 替换模式,最后再改回来

但是,如果您要这样做,并且您的使用可能涉及到的不仅仅是一些简单的字符串(可能您正在过滤数据等),那么与sed一起使用的最佳字符是换行符。这是因为由于sed是100%基于行的,换行符是在获取新行时保证永远不会接收到的唯一字符(在此讨论中忘记GNU多行扩展)。

首先,这里有一个非常简单的方法来解决使用换行符作为中间分隔符的问题:

echo "abbc" | sed -E $'s/ab|bc/\\\n&/g; s/\\nab/bc/g; s/\\nbc/ab/g'

简单带来了一些权衡……如果你有多个变量,就像在你最初的文章中一样,你必须全部输入两次。性能也可能得到一点改善。

使用sed做更多的事情会非常麻烦。即使有一些更高级的功能,如分支控制和保持缓冲区(这在我看来真的很弱),你的选择也非常有限。

只是为了好玩,我想出了这个替代方案,但我不认为我有任何特别的理由推荐它胜过这篇文章前面的那个……如果您真的想在sed中做一些奇特的事情,您必须为分隔符创建自己的“约定”。这对于你最初的帖子来说有点过头了,但是对于那些看到这篇文章并且有更复杂情况的人来说,它可能会激发一些想法。

My convention below was: use multiple newlines to "protect" or "unprotect" the part of the line you're working on. One newline denotes a word boundary. Two newlines denote alternatives for a candidate replacement. I don't replace right away, but rather list the candidate replacement on the next line. Three newlines means that a value is "locked-in", like your original post way trying to do with ab and bc. After that point, further replacements will be undone, because they are protected by the newlines. A little complicated if I don't say so myself... ! sed isn't really meant for much more than the basics.

# Newlines
NL=$'\\\n'
NOT_NL=$'[\x01-\x09\x0B-\x7F]'

# Delimiters
PRE="${NL}${NL}&${NL}"
POST="${NL}${NL}"

# Un-doer (if a request was made to modify a locked-in value)
tidy="s/(\\n\\n\\n${NOT_NL}*)\\n\\n(${NOT_NL}*)\\n(${NOT_NL}*)\\n\\n/\\1\\2/g; "

# Locker-inner (three newlines means "do not touch")
tidy+="s/(\\n\\n)${NOT_NL}*\\n(${NOT_NL}*\\n\\n)/\\1${NL}\\2/g;"

# Finalizer (remove newlines)
final="s/\\n//g"

# Input/Commands
input="abbc"
cmd1="s/(ab)/${PRE}bc${POST}/g"
cmd2="s/(bc)/${PRE}ab${POST}/g"

# Execute
echo ${input} | sed -E "${cmd1}; ${tidy}; ${cmd2}; ${tidy}; ${final}"

下面是ooga答案的一个变体,适用于多个搜索和替换对,而无需检查值如何重用:

sed -i '
s/\bAB\b/________BC________/g
s/\bBC\b/________CD________/g
s/________//g
' path_to_your_files/*.txt

这里有一个例子:

之前:

some text AB some more text "BC" and more text.

后:

some text BC some more text "CD" and more text.

注意\b表示单词边界,这可以防止________干扰搜索(我在Ubuntu上使用GNU sed 4.2.2)。如果不使用单词边界搜索,则此技术可能不起作用。

还要注意,这与删除s/________//g并在命令末尾附加&& sed -i 's/________//g' path_to_your_files/*.txt的结果相同,但不需要两次指定路径。

类似于jthill所建议的,如果知道文件中没有空值,可以使用\x0或_\x0_来代替________。