如何在触发“未终止替代模式”错误的bash脚本中重新组织sed正则表达式中的嵌套引号?

时间:2014-07-20 18:07:54

标签: regex macos bash sed terminal

以下命令在bash中抛出unterminated substitute pattern错误:

eval $(echo "sed '" "s,@\("{a..u}{a..z}"\),\n\n\1,;" "'")

但不适合所有人。 Linux显然运行良好。 Mac抛出未终止的替代模式错误。

如何进行重组以使其发挥作用?

这里是整个bash命令(目标是将当前的MySQL设置干净地输出到my.cnf):

{
  # Print version, user, host and time
  echo -e "# MYSQL VARIABLES {{{1\n##\n# MYSQL `
      mysql -V | sed 's,^.*\(V.*\)\, for.*,\1,'
      ` - By: `logname`@`hostname -f` on `date +%c`\n##"
  for l in {a..z}; do
      # Get mysql global variables starting with $l
      echo '#'; mysql -NBe "SHOW GLOBAL VARIABLES LIKE '${l}%'" |
      # Transorm it
      sed 's,\t,^= ,' |
      column -ts^ |
      tr "\n" '@' |
      eval $(echo "sed '" "s,@\("{a..u}{a..z}"\),\n\n\1,;" "'") |
      eval $(echo "sed '" "s,@\(innodb_"{a..z}{a..z}"\),\n\n\1,;" "'") |
      tr '@' "\n" |
      sed 's,^,# ,g'
  done
  echo -e "#\n##\n# MYSQL VARIABLES }}}1";
} | tee ~/mysql-variables.log

3 个答案:

答案 0 :(得分:5)

OS X中的默认sed是sed的BSD版本。刚刚测试过:

  • 上述操作在OS X的默认sed
  • 中失败
  • 并使用GNU版本(gsed - 从macports安装)。

所以,BSD版本可能不会处理这么长的替换命令序列。

您可以尝试使用下一个:

eval $(echo "perl -ple '" "s,@("{a..u}{a..z}"),\n\n\1,;" "'")

也许我没有理解你的目标,但更简单的是什么呢?

sed 's/@\([a-u][a-z]\)/\n\n\1/' #or
sed 's/@\("[a-u][a-z]"\)/\n\n\1/'

修改

我再一次专注于第一个代码行,而不是整个解决方案。因此创建了一个bash / perl版本,它在OS X上没有问题(使用默认的OS X工具)。

下一个代码

MYSQLCMD=/usr/local/mysql-5.6.16-osx10.7-x86_64/bin/mysql   #your path to mysql command

printf "# MYSQL VARIABLES {{{1\n##\n# MYSQL %s " "$($MYSQLCMD -V | sed 's/.*\(Ver .*\),.*/\1/')"
printf " - By: %s@%s on %s\n" $(logname) $(hostname -f) "$(date +%c)"

perl -e "\$s=qx($MYSQLCMD -NBe 'SHOW GLOBAL VARIABLES');" \
     -e 'for("aa".."uz"){$s=~s/^($_)/#\n$1/m;$s=~s/^(innodb_$_)/#\n$1/m};' \
     -e '$s=~s/(.*)\t(.*)/sprintf "# %-55s= %s",$1,$2/gem;print $s'

printf "#\n##\n# MYSQL VARIABLES }}}1\n";

大致与原始代码完全相同。

答案 1 :(得分:1)

尝试将命令分解为多个命令:

 eval "$(printf "sed "; echo "-e 's,@\("{a..z}{a..z}"\),\n\n\1,'")"

但请注意,OSX上的sed也可能不像\n那样换行,所以你必须这样做:

$ nl='
'
$ eval "$(printf "sed "; echo "-e 's,@\("{a..z}{a..z}"\),\\$nl\\$nl\1,'")"

我强烈建议找一个更好的解决方案。可能是通过perl

答案 2 :(得分:1)

对于快速修复,请尝试(请参阅下文,了解不使用eval的优选替代方案):

eval "$(echo "sed '" "s,@\("{a..u}{a..z}"\),"$'\\\n\\\n'"\1,"$'\n' "'")"

正如@ jm666暗示的那样, FreeBSD sed (至少是OS X 10.9.4附带的版本)对个人的大小有限制脚本中的行(命令字符串) - 4096字节 - 以及使用{{1}产生的单行字符串 }}大括号(范围)扩展(bash超出此限制

上述方法通过附加{a..u}{a..z}s扩展到实际换行符中的将每个$'\n'调用放在自己的行上来解决) - 请参阅<)对于要进行大括号扩展的字符串,而不是bash

另请注意,;已替换为拼接\n\n,因为 FreeBSD sed不支持替换字符串中的$'\\\n\\\n'转义(将它们视为文字\n字符)。 n使用名为ANSI C-quoting的bash功能插入实际换行符 - 使用$'\\\n\\\n'进行转义。

(同样, FreeBSD sed也不支持正则表达式中的转义序列\ 来表示字符,因此您的\t命令必须替换为sed 's,\t,^= ,' 。)

请注意,传递给sed 's,'$'\t'',^= ,'的整个字符串必须是双引号,以确保新行传递到eval

请注意,理论上你仍然可以达到极限:最大值。 命令行的长度,但该限制要高得多:在OS X上略小于256 KB 。 此外,您可以使用sed选项通过文件传递长sed个脚本。 功能


通常情况下,避免使用-f 会更好,所以这里是替代

eval