sed匹配换行符的模式

时间:2016-02-08 20:28:18

标签: regex bash awk sed

以下是我的意见:

<array>
    <string>extra1</string>
    <string>extra2</string>
    <string>Yellow
5</string>

注意:&#34;黄色&#34;之间有空格和换行符。和&#34; 5&#34;

我正在把它传给sed:

| sed -n 's#.*<string>\(.*\)</string>#\1#p'

我得到了输出:

extra1
extra2

我知道,因为sed从每个输入行的末尾剥离换行符,换行符不匹配 - 因此会占用结果。我已经阅读了有关从缓冲区添加下一行的文章,但我无法解决模式匹配中需要使用的内容,以使其工作。

我想要的输出是:

extra1
extra2
Yellow 5

(如果它有所作为,我正在使用Mac,所以我需要这个 - 我认为 - sed的FreeBSD变体。)

当然,如果另一种工具对我想达到的目标更好,我愿意接受建议!谢谢!

6 个答案:

答案 0 :(得分:2)

关闭你的数组标签并尝试使用xmlstarlet和GNU sed:

xmlstarlet sel -t -v "//array/string" input.xml | sed '/ $/{:a;N;s/\n//;ta}'

输出:

extra1
extra2
Yellow 5

答案 1 :(得分:2)

加入线条将它们分开:

tr -d "\n" < file| grep -o "<string>[^<]*</string>"|sed 's/<string>\(.*\)<\/string>/\1/'

答案 2 :(得分:1)

默认情况下,

perl在OSX上可用,因此您可以使用:

perl -0ne 's#<string>([^<]*)</string>#sub{$x=$1;$x=~tr/\n/ /;print $x."\n";}->()#eg' file.xml
extra1
extra2
Yellow 5

或者,您可以使用gnu-awk安装home brew并使用:

awk -v RS= -v FPAT='<string>([^<]*)</string>' 'for(i=1; i<=NF; i++) {
   gsub(/<\/?string>/, "", $i); gsub(/\n/, " ", $i); print $i}}' file.xml
extra1
extra2
Yellow 5

答案 3 :(得分:1)

任何时候你开始谈论&#34;缓冲&#34;或者&#34;持有空间&#34;或者除了s,g和p之外的其他结构(使用-n)你只是使用了错误的工具。所有关于sed的东西在20世纪70年代中期发明时已经过时了,当时发明了awk,所以只需使用awk。这是GNU awk用于多字符RS的一种方式:

$ awk -v RS='</?string>' '!(NR%2){gsub(/\n/," "); print}' file
extra1
extra2
Yellow 5

上述内容只是在将任何换行符转换为空白字符后打印<string></string>之间的任何内容。

对于其他问题,一种方法是:

$ cat tst.awk
{ rec = (rec=="" ? "" : rec " ") $0 }
END {
    split(rec,f,"</?string>")
    for (i=2;i in f;i+=2) {
        print f[i]
    }
}

$ awk -f tst.awk file
extra1
extra2
Yellow 5

答案 4 :(得分:0)

您可以使用xmllint来解决此问题。我稍微修改了你的例子,以便你可以看到发生了什么。

<强>的test.xml

<array>
  <string1>extra1</string1>
  <string2>extra2</string2>
  <string3>Yellow
5</string3>
</array>

由于您希望字符串包含换行符,因此我将此值设为唯一。现在使用xmllintsed来获取结果

[saxdaddy ~]$  x="$(xmllint --xpath "/array/string3" test.xml | sed '/^\/ >/d' | sed 's/<[^>]*.//g')"
[saxdaddy ~]$  echo $x
Yellow 5

xmllint的xpath功能将以字典方式搜索XML。然后sed将删除开头和结尾标记。对此的“技巧”是使用引号捕获变量,然后使用引号来回显结果。

如果您的目标代码在文件路径中不是唯一的,那么您可以制作for循环来查找$'\n'(换行符)并将其设置为您的变量。

答案 5 :(得分:0)

请使用之类的旨在解析xml的工具:

template <typename ...Ts>
static void log(Ts&&... ts)
{
    (do_log(ts), ...);
    std::cout << "\n";
}

// private:
template <typename T>
static void do_log(const T& arg) {
    std::cout << arg;
}

template <typename T>
static void do_log(const std::vector<T>& v)
{
    std::cout << '{';

    const char* sep = "";
    for (const auto& e : v) {
        std::cout << sep;
        do_log(e);
        sep = ", ";
    }
    std::cout << '}';
}

template <typename T>
static bool try_log_any(const std::any& arg) {
    if (auto* p = std::any_cast<T>(&arg)) {
        do_log(*p);
        return true;
    }
    return false;
}

template <typename... Ts>
static bool try_log_any_from(const std::any& arg) {
    if ((try_log_any<Ts>(arg) || ...)) {
        return true;
    }
    std::cout << "unsupported any type" << std::endl;
    return false;
}

static void do_log(const std::any& arg) {
    try_log_any_from<std::string, int, unsigned, char, float, double /*..*/>(arg);
}