Bash - 转义单引号

4

我得到了一个数据库脚本,当给出某个身份证、姓氏和名字的时候,可以搜索重复的ID。以下是其核心代码:

PSQL="psql -p $PGPORT -h $DBHOST -d $DB -tAc "

DUP_ID=$($PSQL "SELECT id FROM inmate WHERE id NOT SIMILAR TO '(0*)${1}' AND lastname ILIKE '${_LNAME}' AND firstname ILIKE '${_FNAME}' LIMIT 1")

这段代码很好用,除非名字中有撇号,例如 "O'Neil"。我尝试使用 \ 转义每一次 ' 的出现,但是并没有成功。我已经花了一整天的时间在论坛上搜索,并尝试着使用不同的方法,但仍然无法在每个 ' 前面添加一个 \。

以下是目前的代码:

local _LNAME=`echo "${2}" | sed "s/'/\\\'/g"`
local _FNAME=`echo "${3}" | sed "s/'/\\\'/g"`
echo -e $_LNAME
echo -e $_FNAME

# Output

O'Neil
Robert

作为常规,提前感谢!


“echo | sed” 是什么意思?Bash 内置参数扩展运算符,用于执行字符串操作;例如,您可以运行:in="'"; out="\\'"; escaped_lname=${lname//$in/$out},这将比尝试使用 sed 玩游戏要快得多且更有效率。 - Charles Duffy
另外,注意环境变量和内置函数通常使用全大写。您脚本中的局部变量应至少包含一个小写字符,以避免与这些类发生命名空间冲突。 - Charles Duffy
1
请记住小博比的表格 - Jonathan Leffler
此外,echo -e(像 echo -n 一样)是非 POSIX 的,通常已被弃用;对于除了非常简单的 echo 使用情况之外,printf 更受欢迎。如果您想以人类可读的方式显示变量的内容,则使用 printf '%q\n' "$_lname" 将是更好的习惯 -- 它甚至可以以格式显示诸如换行符、回车符和其他不可打印字符,以便它们可以重新输入到 shell 中。 - Charles Duffy
2个回答

2
QUERY=(
  SELECT id FROM inmate WHERE
  id NOT SIMILAR TO "'(0*)$1'" AND
  lastname ILIKE "'$_LNAME'" AND
  firstname ILIKE "'$_FNAME'" LIMIT 1
)
psql -p $PGPORT -h $DBHOST -d $DB -tAc "${QUERY[*]}"

1
整洁,而且很少有时候是正确的使用 "${array[@]}" 而不是 "${array[*]}" - Jonathan Leffler
@JonathanLeffler 嗯,我总是尽可能使用 *,它更“简单”,但大多数人不了解其中的区别。 - Zombo
您可以使用${_LNAME//\'/''}将名称中的任何单引号扩展为双引号,以防止小博比表格攻击。我会在某一天解决为什么需要在搜索部分而不是替换部分中使用反斜杠的问题。 - Jonathan Leffler
1
@StevenPenny,“更简单”?我不确定在每个地方都放置$IFS的第一个字符是否确实是简单的模型,特别是如果回到多个单词涉及字符串拆分和通配符扩展步骤,而不是仅在第一次保留原始单词边界。但是,在这种特定用例中,当目标是单个单词时,这是完全可以的。 - Charles Duffy

1

这是传递复杂命令的错误方式:

PSQL="psql -p $PGPORT -h $DBHOST -d $DB -tAc "

相反,使用数组:
single_quote="'"
escaped_single_quote="\\'"
quoted_fname=${1//$single_quote/$escaped_single_quote}
quoted_lname=${2//$single_quote/$escaped_single_quote}
psql=( psql -p "$PGPORT" -h "$DBHOST" -d "$DB" -tAc )
dup_id=( "${psql[@]}" "SELECT id FROM ... WHERE ... '${quoted_lname}'" )

使用"${dup_id[@]}"运行您的命令,将保护您免受shell注入漏洞的影响。它不能保证您免受SQL注入攻击的影响(有太多的方法可以执行这些攻击,并且数据库在字符集转换方面有太多的怪癖,不能信任此处使用的基于字符替换的转义来对抗恶意数据),但是,好吧,这就是为什么关心正确性或安全性的人使用支持绑定参数的语言来生成SQL查询的原因--bash不是其中的一员。

参见BashFAQ #50,以及freenode.org #bash频道维基的BashWeaknesses页面--后者明确指出bash无法胜任生成SQL的任务。


非常好的答案,除了您需要通过双引号转义单引号。 - Jeff
@Jeff,嗯?在bash中,“''”与根本没有引号是完全相同的。试一下:printf'%s\n' ''*''会打印本地目录中的所有文件名,就像printf'%s\n'*一样,而不是打印由单引号包围的“*”。 - Charles Duffy
@Jeff,如果你指的是正确的SQL引用而不是bash引用,你可能是对的,escaped_single_quote="''"会更正确 - 但这意味着问题本身要求错误的东西,因为OP明确问如何添加前导反斜杠,而不是如何双引号或者(更普遍地)生成字符串的正确SQL引用。 - Charles Duffy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接