需要在文本文件中插入单引号以用作使用 sed 的 SQL 查询

需要在文本文件中插入单引号以用作使用 sed 的 SQL 查询

我创建了一行如下所示的文本:

INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,,00:23:32:c2:a9:e8,Auth-Type,:=,Accept);

我想使用 sed 使其看起来像这样:

INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,'','00:23:32:c2:a9:e8','Auth-Type',':=','Accept');

这在上下文中更有意义,并且在过去(显然)17 小时内我已经对此有了更深入的了解:

#!/bin/bash
ssh [email protected] brmacs >>MACS.txt mv MACS.txt /etc/persistent scp [email protected]:/etc/persistent/MACS.txt MACS.txt
sed -i "1d" MACS.txt
head -c 58 MACS.txt >>shortmacs.txt
tail -c 18 shortmacs.txt >>usermac.txt
sed 's/"//g' usermac.txt >>usermacrdy.txt
sed -i 's/^/INSERT INTO `radcheck`(`id`, `username`, `attribute`, `op`, `value`) VALUES (,'',/' usermacrdy.txt
sed "s/$/','Auth-Type',':=','Accept');/" usermacrdy.txt > sqlquery.txt
sed -i "s/,,/\,\'\',\'/" sqlquery.txt
rm -f MACS.txt
rm -f shortmacs.txt
rm -f usermac.txt
rm -f usermacrdy.txt

作品!!!

头部和尾部从 UBNT CPE 设备传输过来的原始文本文件中剪切出 MAC 地址,然后通过 sed 将其传递以围绕 MAC 地址构建 SQL 语法。

毕竟,我发现id查询的部分并不是成功所必需的,所以现在我的处境略有相同:

sed -i 's/^/INSERT INTO `radcheck`(`username`, `attribute`, `op`, `value`) VALUES (/' usermacrdy.txt
sed "s/$/','Auth-Type',':=','Accept');/" usermacrdy.txt > sqlquery.txt
sed -i "s/\,\'\',\'//" sqlquery.txt

答案1

有四种方法可以包含您需要的单引号。

无法转义单引号字符串中的单引号字符串。但是,可以结束带引号的字符串,插入转义的单引号,然后开始一个新的单引号字符串。因此,要将单引号放在 中间'ab',请使用:'a'\''b'。或者,使用您需要的 sed 命令:

$ sed -r 's/,([^ ),]+)/,'\''\1'\''/g; s/,,/,'\'\'',/g' file
INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,'','00:23:32:c2:a9:e8','Auth-Type',':=','Accept');

第二种方法是使用双引号字符串,在这种情况下可以轻松插入单引号:

$ sed -r "s/,([^ ),]+)/,'\1'/g; s/,,/,'',/g" file
INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,'','00:23:32:c2:a9:e8','Auth-Type',':=','Accept');

双引号字符串的问题在于 shell 对它们进行处理。不过,这里没有 shell 活动字符,因此很容易。

第三种方法是使用十六进制转义,如 PM2Ring 所示。

Jonathan Leffler 在评论中建议的第四种方法是将sed命令放在单独的文件中:

$ cat script.sed 
s/,([^ ),]+)/,'\1'/g
s/,,/,'',/g
$ sed -rf script.sed file
INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,'','00:23:32:c2:a9:e8','Auth-Type',':=','Accept');

这种方式的优点是可以sed直接读取命令,不受shell的干扰。因此,这完全避免了转义 shell 活动字符的需要,并允许以纯语法输入命令sed

该解决方案如何sed运作

诀窍是在您想要的逗号分隔字符串周围放置单引号,但不要在其他字符串周围放置单引号。根据您提供的单个示例,这是一种方法:

  • s/,([^ ),]+)/,'\1'/g

    这会查找逗号后面的一个或多个非空格、非逗号和非右括号字符。这些字符放在单引号内。

  • s/,,/,'',/g

    这会查找连续的逗号并在它们之间放置两个单引号。

OSX 和其他 BSD 平台

为了避免额外的反斜杠,上述sed表达式使用扩展正则表达式。对于 GNU,它们被调用为-r,但对于 BSD,它们被调用为-E.此外,某些非 GNUsed不接受用分号分隔的多个命令。因此,在 OSX 上,尝试:

sed -E -e "s/,([^ ),]+)/,'\1'/g" -e "s/,,/,'',/g" file

附录:匹配 MAC 地址

从评论中,我们得到以下输入;

$ cat file3
 INSERT INTO radcheck(username, attribute, op, value) VALUES (00:23:32:c2:a9:e8,'Auth-Type',':=','Accept'); 

并且,我们希望在左括号后面的 MAC 地址两边加上单引号。要做到这一点:

$ sed -r "s/\(([[:xdigit:]:]+)/('\1'/" file3
 INSERT INTO radcheck(username, attribute, op, value) VALUES ('00:23:32:c2:a9:e8','Auth-Type',':=','Accept'); 

在任何区域设置中,[:xdigit:]将匹配任何十六进制数字。因此,([[:xdigit:]:]+)将匹配 MAC 地址(十六进制数字或冒号)。

答案2

当你在全球范围内工作时,你必须考虑如何sed阅读。局部替换g会将模式空间划分为根据您的规范分隔的各个字段,并对每个字段进行操作。分隔符按读取顺序从左到右进行识别,并将sed尽快应用每个操作。这是一个s///可以满足您要求的替代语句:

sed "s/\(([^)]*)[^,]*\)\{0,1\},\([^,)]*\)/\1,'\2'/g
" <<\IN
INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,,00:23:32:c2:a9:e8,Auth-Type,:=,Accept);

运行时,打印:

INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,'','00:23:32:c2:a9:e8','Auth-Type',':=','Accept');

就是这样:

  • \(([^)]*)[^,]*\)\{0,1\}*- 最重要的是零+匹配和其他类型的分布。整个块是可选的 -sed如果有机会,它会很高兴地在其位置匹配一个空字符串 - 而且它实际上在很多时候都这样做,否则这根本不起作用。但因为\{0,1\}用过sed不是匹配更多的比像这样的整个序列。这就是为什么分布很重要。在这个块中我们有:
    • (- 至少一个左括号。
    • [^)]*- 零+非右括号。
    • )- 一个右括号
    • [^,]*- 零+非逗号。
  • sed将按顺序匹配其中的每一个 - 并且作为一个块,因为它们被\(分组\)。零*+匹配是非常在全球范围内都很重要 - 如果您+在该设置中至少使用一个彻底地降低应用编辑的效率。对于上面的块的输入,sed这样分隔您的字符串:

  • INSERT INTO radcheck- 这个字符串来了我们的任何匹配项并且不包含其中任何一个。它被忽略。

  • (id, username, attribute, op, value) VALUES (- 这个字符串构成了我们整个*可选的匹配块。首先sed匹配the ,然后匹配then(的整个字符串,最后 - 并且重要的是 - 整个字符串包含我们[^)]*)[^,]*超过的另一个匹配案例(。它现在无法匹配 - 因此跳过 -(该可选匹配组的下一个。sed将尝试下一个。

当在第二个序列上sed应用替换时,它将\2(.,.,.)仍然\1也应用替代品,因为它仍然匹配 \1。我首先匹配\1全部的 (.,.,.)..(块,将其保存\1并用 - 替换\1为自身。从第二个序列的开头到下一个出现的序列,每次匹配一个(即每次匹配逗号时) ( sed都会替换为空字符串。\1

假设一行中有许多这样的序列 - 并且假设逗号仅出现在( )分隔符内,sed则将为每个序列交替引用。这是您的多次示例:

INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,'','00:23:32:c2:a9:e8','Auth-Type',':=','Accept');INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,'','00:23:32:c2:a9:e8','Auth-Type',':=','Accept');INSERT INTO radcheck(id, username, attribute, op, value) VALUES (,'','00:23:32:c2:a9:e8','Auth-Type',':=','Accept');
  • ,\([^,)]*\)- 请注意,唯一的不是整个正则表达式的可选匹配是,逗号。另一个块在表达式和输入中出现在它之前,因此sed允许它应用 - 但当其他条件都相等时,逗号优先,这是sed''随处插入 s 的唯一原因,因为它匹配所有空字符串在其他两个块上的每个字节之间做不是sed在应用 -分隔时匹配空字符串。
  • sed将继续匹配\2- 并保存到其中 - 每个 not)和 not的字符序列,。它将替换遇到的每个逗号,'\2'。它将继续以这种方式,直到它遇到(能够填充的点\1,并且会这样做。

答案3

尝试这个:

sed 's/,,/,\x27\x27,/'

\x27是十六进制转义码'

演示

$ echo 'INSERT INTO `radcheck`(`id`, `username`, `attribute`, `op`, `value`) VALUES (,,00:23:32:c2:a9:e8,Auth-Type,:=,Accept);' |
> sed 's/,,/,\x27\x27,/'
INSERT INTO `radcheck`(`id`, `username`, `attribute`, `op`, `value`) VALUES (,'',00:23:32:c2:a9:e8,Auth-Type,:=,Accept);

相关内容