“sed”特殊字符处理

我们的脚本中有一个sed命令,用来自variables的值replace文件内容

例如..

export value="dba01upc\Fusion_test" sed -i "s%{"sara_ftp_username"}%$value%g" /home_ldap/user1/placeholder/Sara.xml 

sed命令会忽略像'\'这样的特殊字符,而用'\'replace为string“dba01upcFusion_test”,它会起作用如果我执行export export ='dba01upc \ Fusion_test'(用'\'围住'')。但不幸的是,我们的客户想要将原始文本dba01upc \ Fusion_test与单/双引号导出,并且他不想向文本添加任何额外的字符。 任何一个可以让我知道如何使sed文字放置特殊字符..

replace之前:Sara.xml

 <?xml version="1.0" encoding="UTF-8"?> <ser:service-account > <ser:description/> <ser:static-account> <con:username>{sara_ftp_username}</con:username> </ser:static-account> </ser:service-account> 

replace后:Sara.xml

 <?xml version="1.0" encoding="UTF-8"?> <ser:service-account> <ser:description/> <ser:static-account> <con:username>dba01upcFusion_test</con:username> </ser:static-account> </ser:service-account> 

提前致谢

你不能用sed强有力的解决这个问题。 只需使用awk:

 awk -v old="string1" -v new="string2" ' idx = index($0,old) { $0 = substr($0,1,idx-1) new substr($0,idx+length(old)) } 1' file 

啊,@ mklement0有一个很好的观点 – 为了避免被解释的转义,你需要传递arg列表中的值以及文件名,然后从中分配变量,而不是使用-v (看到我写了很长一段时间的comp.unix.shell常见问题的总结http://cfajohnson.com/shell/cus-faq-2.html#Q24,但显然忘记了!)&#x3002;

以下内容将对每一行上的每个搜索字符串进行强有力的替换( a\ta – > e\tf ):

 $ cat tst.awk BEGIN { old=ARGV[1]; delete ARGV[1] new=ARGV[2]; delete ARGV[2] lgthOld = length(old) } { head = ""; tail = $0 while ( idx = index(tail,old) ) { head = head substr(tail,1,idx-1) new tail = substr(tail,idx+lgthOld) } print head tail } $ cat file a\ta aaa\ta $ awk -f tst.awk 'a\ta' 'e\tf' file e\tf aae\tf 

file的空白是制表符。 您可以将ARGV [3]向下移动,并根据需要调整ARGC,但在大多数情况下不需要。

更新与事后的好处,目前的选择

  • 更新2 :如果您打算使用sed请参阅下面的 – 有点麻烦,但现在是强大的和通用的解决方案
  • 如果你想要一个健壮的,自给自足的awk解决方案 ,同时也可以正确处理任意搜索和替换字符串(但不能包含诸如边界断言之类的正则表达式),请参阅Ed Morton的答案 。
  • 如果你想要一个纯粹的bash解决方案,并且你的输入文件很小 ,保留多个尾随换行符并不重要,请参阅Charles Duffy的答案 。
  • 如果您需要一个完整的第三方模板解决方案 ,请考虑j2cli ,一个Jinja2的模板化CLI – 如果您有Python和pip ,请使用sudo pip install j2cli
    简单的例子(请注意,由于替换字符串是通过文件提供的,所以这可能不适用于敏感数据;请注意括号( {{...}} ):

     value='dba01upc\Fusion_test' echo "sara_ftp_username=$value" >data.env echo '<con:username>{{sara_ftp_username}}</con:username>' >tmpl.xml j2 tmpl.xml data.env # -> <con:username>dba01upc\Fusion_test</con:username> 

如果您使用sed ,则需要仔细转义搜索和替换字符串 ,因为:

  • 正如Ed Morton在别处注释中所指出的那样, sed不支持使用文字字符串作为替换字符串 – 它总是翻译替换字符串中的特殊字符/序列。
  • 类似地, 搜索字符串文字必须以其字符不会被误认为特殊的正则表达式字符的方式进行转义。

以下使用了两个通用的帮助函数来执行这个转义(引用)这些转义应用了“可以用sed可靠地转义正则表达式字符吗? :

 #!/usr/bin/env bash # SYNOPSIS # quoteRe <text> # DESCRIPTION # Quotes (escapes) the specified literal text for use in a regular expression, # whether basic or extended - should work with all common flavors. quoteRe() { sed -e 's/[^^]/[&]/g; s/\^/\\^/g; $!a\'$'\n''\\n' <<<"$1" | tr -d '\n'; } # ' # SYNOPSIS # quoteSubst <text> # DESCRIPTION # Quotes (escapes) the specified literal string for safe use as the substitution string (the 'new' in `s/old/new/`). quoteSubst() { IFS= read -d '' -r < <(sed -e ':a' -e '$!{N;ba' -e '}' -e 's/[&/\]/\\&/g; s/\n/\\&/g' <<<"$1") printf %s "${REPLY%$'\n'}" } # The search string. search='{sara_ftp_username}' # The replacement string; a demo value with characters that need escaping. value='&\1%"'\'';<>/|dba01upc\Fusion_test' # Use the appropriately escaped versions of both strings. sed "s/$(quoteRe "$search")/$(quoteSubst "$value")/g" <<<'<el>{sara_ftp_username}</el>' # -> <el>&\1%"';<>/|dba01upc\Fusion_test</el> 
  • quoteRe()quoteSubst() 正确处理多行字符串
    • 但是请注意,假设sed默认只读一行,那么在多行字符串中使用quoteRe()只在同时显式读取多行(或所有行)的sed命令中才有意义。
  • quoteRe()总是可以安全地使用命令替换( $(...) ,因为它总是返回一个单行字符串(输入中的换行符被编码为'\n' )。
  • 相反,如果将quoteSubst()与具有尾随换行符的字符串一起使用,则不得使用$(...) ,因为后者将删除最后一个尾随的换行符,并因此中断编码(因为quoteSubst()转义实际换行符,返回的字符串将在一个悬而未决的\ )。
    • 因此,对于带有换行符的字符串,使用IFS= read -d '' -r escapedValue <(quoteSubst "$value")将转义值读入单独的变量中,然后在sed命令中使用该变量。

这可以通过bash buildins来完成 – 没有sed,awk等。

 orig='{sara_ftp_username}' # put the original value into a variable new='dba01upc\Fusion_test' # ...no need to 'export'! contents=$(<Sara.xml) # read the file's content into new_contents=${contents//"$orig"/$new} # use parameter expansion to replace printf '%s' "$new_contents" >Sara.xml # write new content to disk 

有关使用参数扩展进行字符串替换的信息,请参阅BashFAQ#100的相关部分 。