【问题标题】:Using sed to replace a string with the contents of a variable, even if it's an escape character使用 sed 将字符串替换为变量的内容,即使它是转义字符
【发布时间】:2011-01-16 12:15:21
【问题描述】:

我正在使用 sed -e "s/\*DIVIDER\*/$DIVIDER/g"*DIVIDER* 替换为用户指定的字符串,该字符串存储在$DIVIDER 中。问题是我希望他们能够指定转义字符作为分隔符,如 \n 或 \t。当我尝试这个时,我只是以字母 n 或 t 结尾,等等。

有人对如何做到这一点有任何想法吗?将不胜感激!

编辑:这是脚本的核心,我一定是遗漏了一些东西。

curl --silent "$URL" > tweets.txt

if [[ `cat tweets.txt` == *\<error\>* ]]; then
    grep -E '(error>)' tweets.txt | \
    sed -e 's/<error>//' -e 's/<\/error>//' |
    sed -e 's/<[^>]*>//g' |

head $headarg | sed G | fmt

else
    echo $REPLACE | awk '{gsub(".", "\\\\&");print}'
    grep -E '(description>)' tweets.txt | \
    sed -n '2,$p' | \
    sed -e 's/<description>//' -e 's/<\/description>//' |
    sed -e 's/<[^>]*>//g' |
    sed -e 's/\&amp\;/\&/g' |
    sed -e 's/\&lt\;/\</g' |
    sed -e 's/\&gt\;/\>/g' |
    sed -e 's/\&quot\;/\"/g' |
    sed -e 's/\&....\;/\?/g' |
    sed -e 's/\&.....\;/\?/g' |
    sed -e 's/^  *//g' |
    sed -e :a -e '$!N;s/\n/\*DIVIDER\*/;ta' |   # Replace newlines with *divider*.
    sed -e "s/\*DIVIDER\*/${DIVIDER//\\/\\\\}/g" |          # Replace *DIVIDER* with the actual divider.

    head $headarg | sed G
fi

一长串 sed 行正在替换来自 XML 源的字符,最后两个是应该用指定字符替换换行符的行。我知道用另一个换行符替换一个换行符似乎是多余的,但这是我能想到的让他们选择自己的分隔符的最简单方法。分隔符替换非常适合普通字符。

【问题讨论】:

  • 您的脚本写得不好。很多不必要的 sed 步骤。显示您正在处理的输入文件,并显示您想要的输出。

标签: bash string sed


【解决方案1】:

您可以像这样使用 bash 转义反斜杠:

sed -e "s/\*DIVIDER\*/${DIVIDER//\\/\\\\}/g"

语法是${name/pattern/string}。如果模式以/ 开头,则name 中出现的每一个pattern 都会被string 替换。否则只替换第一个出现的位置。

【讨论】:

  • 有了这个,我最终会在流中插入一个双反斜杠和一个“n”。也许这就是我管东西的方式。我将编辑原始帖子以包含更多脚本。
  • tangens 的解决方案处理反斜杠,但不处理 \n 或 \t。问题是 sed 无法识别 \n 或 \t。您可以将它们显式地放在 DIVIDER 中,也可以将输出通过另一个过滤器通过管道将 \n 替换为换行符。例如:sed 's/\\n/\/g'
  • 这是个好主意,我可以在输出时通过 tr 管道返回。谢谢!
【解决方案2】:

也许:

case "$DIVIDER" in
(*\\*) DIVIDER=$(echo "$DIVIDER" | sed 's/\\/\\\\/g');;
esac

我玩过这个脚本:

for DIVIDER in 'xx\n' 'xxx\\ddd' "xxx"
do
    echo "In:  <<$DIVIDER>>"
    case "$DIVIDER" in     (*\\*) DIVIDER=$(echo "$DIVIDER" | sed 's/\\/\\\\/g');;
    esac
    echo "Out: <<$DIVIDER>>"
done

在 MacOS X 上使用“ksh”或“bash”(但不是“sh”)运行:

In:  <<xx\n>>
Out: <<xx\\n>>
In:  <<xxx\\ddd>>
Out: <<xxx\\\\ddd>>
In:  <<xxx>>
Out: <<xxx>>

【讨论】:

    【解决方案3】:

    这似乎是一个简单的替换:

    $ d='\n'
    $ echo "a*DIVIDER*b" | sed "s/\*DIVIDER\*/$d/"
    a
    b
    

    也许我不明白你想要完成什么。

    那么也许这一步可以代替你的最后两个:

    sed -n ":a;$ {s/\n/$DIVIDER/g;p;b};N;ba"
    

    注意美元符号后面的空格。它可以防止 shell 将“${s...”解释为变量名。

    正如 ghostdog74 所建议的,您拨打sed 的电话太多了。您可以将许多管道字符更改为反斜杠(续行),并从除第一个字符之外的所有字符中删除“sed”(将“-e”留在任何地方)。 (未经测试)

    【讨论】:

    • 感谢您提供有关不必要的 seds 调用的信息!我很久以前写过这篇文章,对 sed 或 shell 脚本一般不太了解。
    • 我已经尝试将调用与 continuation 和全部合并在一条线上,但一些替换停止工作。它不再删除 标记或多余的空间。他们需要遵循某种神奇的顺序吗?
    • 你把这个留在原地了吗? sed -n '2,$p' | sed ... 您正在通过这样做来选择要处理的行。如果你在这里取出管道,它可能不起作用。如果没有看到数据和修改后的脚本,我不能肯定地说。你应该知道这种疯狂的方式在于(在 HTML 上使用正则表达式):stackoverflow.com/questions/1732348/…
    【解决方案4】:

    你只需要转义转义字符。

    \n 将匹配 \n

    \ 将匹配 \

    \\ 将匹配 \

    【讨论】:

    • 我刚刚尝试了 \\\n,它确实以 \n 结尾,但它按字面意思打印。如何让 sed 将其解释为转义而不是普通字符串?
    【解决方案5】:

    使用 FreeBSD sed(例如在 Mac OS X 上)您必须预处理 $DIVIDER 用户输入:

    d='\n'
    d='\t'
    NL=$'\\\n'
    TAB=$'\\\t'
    d="${d/\\n/${NL}}"
    d="${d/\\t/${TAB}}"
    echo "a*DIVIDER*b" | sed -E -e "s/\*DIVIDER\*/${d}/"
    

    【讨论】:

      猜你喜欢
      • 2015-04-18
      • 1970-01-01
      • 1970-01-01
      • 2021-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-11
      • 2019-11-15
      相关资源
      最近更新 更多