【问题标题】:How can I reorganize nested quotes within sed regex in a bash script that triggers an "unterminated substitute pattern" error?如何在触发“未终止替换模式”错误的 bash 脚本中重新组织 sed 正则表达式中的嵌套引号?
【发布时间】:2014-09-11 06:25:38
【问题描述】:

以下命令在 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

【问题讨论】:

  • 我提供的原始答案是有效的。切断输出的新问题是完全独立的,与sed 's,\t,^= ,' 有关
  • 你为什么在这里使用eval

标签: regex macos bash sed terminal


【解决方案1】:

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,但是在sed 中使用字符类远没有让 bash 进行扩展有趣! (而且更聪明!)
  • 虽然我同意您的 sed 命令要简单得多,但我认为我们希望通过 {a..u}{a..z} 扩展发生的每个替换仅按给定顺序发生一次。这背后的想法是将文件分成包含以aa…开头的行,然后是ab…等的节。所以我们只想在第一次出现字母对之前添加新行。
  • @Ryan 添加了完整的解决方案
  • 很棒的工作@jm666!是否可以将命令行转换为单行?
  • @Ryan 不知道 commandline.fu,但请随意,好吧 :) 老实说,也许它可以做得更好......我不是 perl 专家 ;)
【解决方案2】:

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

 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

【讨论】:

  • 您的修复解决了替代错误(甚至是第一个错误),但是当我运行整个脚本时,它会出于某种原因替换 t's。 wait 变为 waithread 变为 hread。你能看出在哪里/为什么会发生这种情况吗?
  • @Ryan,它替换了t,因为您将其替换为=
  • 由于sed 's,\t,^= ,' 这一行而发生了替换。您需要用文字标签替换 \t,类似于我用文字换行符替换 \n 的方式。
  • 不同的 sed 对待 \t\n 的方式不同,这是使用不同工具的好理由。
  • 我不认为对\n\t等控制字符转义序列的不同支持问题本身足以避免@ 987654339@: ANSI-C quoting in bash(以及 kshzsh)允许在 literal 控制字符中进行拼接,这应该适用于所有平台。例如,sed 's,\t,^= ,' 必须写为 sed 's,'$'\t'',^= ,'
【解决方案3】:

对于快速修复,请尝试(请参阅下面的不使用eval 的更可取的替代方案):

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

正如@jm666 所暗示的,FreeBSD sed(至少是 OS X 10.9.4 附带的版本)individual 的大小有限制 em> 脚本中的行(命令字符串) - 4096 字节 - 以及您使用 bash 产生的 单行 字符串大括号(范围)扩展 ({a..u}{a..z}) 超出限制

上述解决方法是通过附加$'\n'(在bash 中扩展为实际的换行符 - 见下文)而不是@987654332,将每个s 调用放在自己的行上 @ 指向要大括号展开的字符串。

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

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

请注意,传递给eval 的整个字符串必须被双引号,以确保将换行符传递给sed

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


一般来说,最好避免使用eval,所以这里有一个替代方法

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

【讨论】:

  • 谢谢你的解释,我终于明白为什么我在Linux下无法重现original question的bug了。
  • @Qeole:我的荣幸;感谢您解释为什么 jm666 的 sed-only 解决方案毕竟可能不是正确的方法(我会修改我的答案)。
  • 我建议there,以防你有兴趣。
猜你喜欢
  • 1970-01-01
  • 2019-02-15
  • 1970-01-01
  • 2014-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-22
  • 2014-12-23
相关资源
最近更新 更多