我一直在尝试调用一个包含来自Windows环境variables的多字节字符的参数,但是还没有find可行的解决scheme。 这是我至今能够debugging的东西。
为了简单起见,假定我有一个名为“Seán”的目录,我试图用它作为exec的一个参数。 如果我只是打电话
exec 'script', "Se\u00E1n".encode("IBM437")
被执行的脚本找不到该文件,因为arg被调整为重音字符丢失。 如果我按照下面的方法进行操作,但是这样做是不好的做法,因为在进入shell之前,arg应该被转义。
exec "script #{"Se\u00E1n".encode("IBM437")}"
所以我的想法是,我只是使用shellescape来保护exec的使用。
require 'shellwords' exec "script #{"Se\u00E1n".encode("IBM437").shellescape}"
但问题是,它逃脱了特殊的性格,所以它看起来像下面这样 – “Se \án”。 我想清楚这是在发生什么,它是从这个正则expression式来的 。
str.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/, "\\\\\\1")
乍看之下,似乎是逃避了已知的一组好的shell字符中的字符。 不幸的是,这套不包括特殊字符,所以我遇到了问题。
我正在寻找的是一个正则expression式,它会执行shell转义,不会混淆特殊字符,以便在将它们传递给exec之前可以将这些参数转义。
正则表达式/([^A-Za-z0-9_\-.,:\/@\n])/
只处理ASCII字母和数字,不是所有的Unicode字母。 [^...]
是否定字符类 ,它匹配除了在类中指定的字符以外的所有字符。 所以,所有的Я
, Ц
, Ą
都被删除,因为它们与[A-Za-z]
不匹配。
你需要的是添加速记类来排除所有的Unicode字母和数字 。 为了使它更安全,我们可以添加一个变音符号类以便保留变音符号:
str.gsub(/([^\p{L}\p{M}\p{N}_.,:\/@\n-])/, "\\\\\\1")
在这里, \p{L}
匹配所有Unicode基本字母, \p{M}
匹配所有的变音符号, \p{N}
匹配任何Unicode数字。
请注意,连字符在放置在字符类的开始/结尾处(或在有效范围或简写字符类之后)时无需转义。
转义字符
码
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 (eg "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
ideone演示
元字符
考虑到应该转义的shell元字符:
# & % ; ` | * ? ~ < > ^ ( ) [ ] { } $ \ \u00FF
我们可以在字符类中包含每个字符:
[#&%;`|*?~<>^()\[\]{}$\\\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