我一直在尝试使用包含多字节字符的参数调用exec,这些字符来自Windows上的环境变量,但尚未找到可用的解决方案。这是我迄今为止能够调试的内容。
为了简单起见,假设我有一个名为“Seán”的目录,我试图将其用作exec的参数。如果我只是打电话
exec 'script', "Se\u00E1n".encode("IBM437")
执行的脚本无法找到该文件,因为arg会以重音字符丢失的方式进行调整。如果我执行以下操作它会起作用,但这是不好的做法,因为arg应该在转到shell之前进行转义。
exec "script #{"Se\u00E1n".encode("IBM437")}"
所以我的想法是我只会使用shellescape来保护exec的使用。
require 'shellwords'
exec "script #{"Se\u00E1n".encode("IBM437").shellescape}"
但问题是它逃脱了特殊字符,因此它看起来如下 - “Se \án”。我想出了这发生的地方,它来自这个regular expression。
str.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/, "\\\\\\1")
乍一看似乎是逃避了一个已知良好的shell字符集中的字符。不幸的是,这套不包含特殊字符,所以我遇到了问题。
我正在寻找的是一个可以执行shell转义的正则表达式,它不会弄乱特殊字符,这样我就可以在将这些args传递给exec之前将其转义。
答案 0 :(得分:1)
转义字符
<强>代码强>
String.class_eval do
def escapeshell()
# Escape shell special characters
self.gsub!(/[#-&(-*;<>?\[-^`{-~\u00FF]/, '\\\\\0')
# Escape unbalanced quotes (single and double quotes)
self.gsub!(/(["'])(?:([^"']*(?:(?!\1)["'][^"']*)*)\1)?/) do
if $2.nil?
'\\' + $1
else
# and escape quotes inside (e.g. "x'x" or 'y"y')
qt = $1
qt + $2.gsub(/["']/, '\\\\\0') + qt
end
end
self
end
end
# Test it
str = "(dir *.txt & dir \"\\some dir\\Sè\u00E1ñ*.rb\") | sort /R >Filé.txt 2>&1"
puts 'String:'
puts str
puts "\nEscaped:"
puts str.escapeshell
<强>输出强>
String:
(dir *.txt & dir "\some dir\Sèáñ*.rb") | sort /R >Filé.txt 2>&1
Escaped:
\(dir \*.txt \& dir "\\some dir\\Sèáñ\*.rb"\) \| sort /R \>Filé.txt 2\>\&1
<强>元字符强>
考虑应该转义的shell元字符:
# & % ; ` | * ? ~ < > ^ ( ) [ ] { } $ \ \u00FF
我们可以在character class中包含每个字符:
[#&%;`|*?~<>^()\[\]{}$\\\u00FF]
与...完全相同:
/[#-&(-*;<>?\[-^`{-~\u00FF]/
然后,我们使用gsub!()
在类中的任何字符之前添加反斜杠:
str.gsub!(/[#-&(-*;<>?\[-^`{-~\u00FF]/, '\\\\\0')
<强>行情强>
只需要转义不平衡报价。这对于保留命令的参数很重要。使用以下表达式,我们匹配平衡报价:
/(["'])[^"']*(?:(?!\1)["'][^"']*)*)\1/
以及不平衡,使最后一部分可选
/(["'])(?:[^"']*(?:(?!\1)["'][^"']*)*)\1)?/
但我们还需要在另一对中避开引号。这是双引号内的单引号,反之亦然。因此,我们将嵌套另一个gsub()
以替换匹配在引号内的文本($2
):
str.gsub!(/(["'])(?:([^"']*(?:(?!\1)["'][^"']*)*)\1)?/) do
if $2.nil?
'\\' + $1
else
qt = $1
qt + $2.gsub(/["']/, '\\\\\0') + qt
end
end
答案 1 :(得分:1)
正则表达式/([^A-Za-z0-9_\-.,:\/@\n])/
仅处理ASCII字母和数字,而不是所有Unicode字母。 [^...]
是negated character class,匹配除中指定的字符之外的所有字符。因此,所有Я
,Ц
,Ą
都会被该表达式删除,因为它们与[A-Za-z]
不匹配。
您需要添加速记类以排除所有Unicode字母和数字。为了使它更安全,我们可以添加一个变音符号类,以便保持变音符号:
str.gsub(/([^\p{L}\p{M}\p{N}_.,:\/@\n-])/, "\\\\\\1")
此处,\p{L}
匹配所有Unicode基本字母,\p{M}
匹配所有变音符号,\p{N}
匹配任何Unicode数字。
请注意,当放置在字符类的开头/结尾(或在有效范围或速记字符类之后)时,不需要转义连字符。