den*_*zer 1 bash shell-script quoting
我想在 bash 脚本中运行这样的命令:
freebcp <authentication and other parameters> -t "," -r "\r\n"
Run Code Online (Sandbox Code Playgroud)
直接在命令行上运行时,该命令按预期工作。但是当放在 bash 脚本中的变量中时,它会返回如下错误:
Msg 20104, Level 3
Unexpected EOF encountered in bcp datafile
Msg 20074, Level 11
Attempt to bulk copy an oversized row to the server
bcp copy in failed
Run Code Online (Sandbox Code Playgroud)
当命令放在变量中并且双引号被转义时:
cmd="freebcp ${db_name}.dbo.${table_name} in ${p_file} -S ${server_name} -U ${username} -P ${password} -t \",\" -r \"\r\n\" -c"
`$cmd`
Run Code Online (Sandbox Code Playgroud)
注意:将以下内容按预期工作:
`freebcp ${db_name}.dbo.${table_name} in ${p_file} -S ${server_name} -U ${username} -P ${password} -t "," -r "\r\n" -c`
Run Code Online (Sandbox Code Playgroud)
所以我知道有一些引用/转义/扩展问题,但我不知道如何解决它。
注 2:单引号 -t -r 参数也不起作用
简短回答:请参阅BashFAQ #50:我试图将命令放入变量中,但复杂的情况总是失败!.
长答案:shell 在解析命令行的过程中会进行变量扩展——特别是在它处理引号和转义之后。因此,将引号和转义符放在变量中与将它们直接放在命令行上的作用不同。
您的答案中的解决方案(将转义字符加倍)会起作用(在大多数情况下),但不是因为您认为它起作用的原因,这让我很紧张。命令:
cmd="freebcp ... -t "," -r "\\r\\n" -c"
Run Code Online (Sandbox Code Playgroud)
被解析为双引号字符串freebcp ... -t,后跟不带引号的字符串,,后跟双引号的字符串-r, 后跟未加引号的字符串 '\\r\\n'(它未加引号的事实是您需要加倍转义的原因),然后是双引号字符串 ' -c'。你打算成为字符串一部分的双引号不被视为字符串的一部分,它们被视为改变字符串不同部分解析方式的分隔符(实际上与预期效果几乎相反)。这样做的原因是双引号实际上在原始命令中没有太大作用,因此反转它们的作用并没有多大作用。删除它们实际上会更好(尽管只是内部的),因为它不会对真正发生的事情产生误导。那会起作用,但它会很脆弱——它起作用的唯一原因是你没有
有几个更好的选择:
根本不要将命令存储在变量中,直接执行即可。存储命令很棘手(正如您所发现的),如果您真的不需要,那就不要。
使用一个函数。如果您正在执行类似反复执行相同命令的操作,请将其定义为一个函数并使用它:
loaddb() {
freebcp "${db_name}.dbo.${table_name}" in "${p_file}" -S "${server_name}" -U "${username}" -P "${password}" -t \",\" -r \"\r\n\" -c"
}
loaddb
Run Code Online (Sandbox Code Playgroud)
请注意,我在所有变量引用周围都使用了双引号——这通常是良好的脚本编写卫生,以防它们中的任何一个包含空格、通配符或 shell在变量值中解析的任何其他内容。
使用数组而不是普通变量。如果您正确执行此操作,每个命令参数都会作为数组的一个单独元素存储,然后您可以使用习语扩展它以"${arrayname[@]}"使其完整无缺:
cmdarray=(freebcp "${db_name}.dbo.${table_name}" in "${p_file}" -S "${server_name}" -U "${username}" -P "${password}" -t \",\" -r \"\r\n\" -c")
"${cmdarray[@]}"
Run Code Online (Sandbox Code Playgroud)
再次注意双引号的大量使用;在这里,它们用于确保正确定义数组元素,而不是作为存储在数组中的值的一部分。另外,请注意数组并非在所有 shell 中都可用;确保您使用的是 bash 或 zsh 或类似的东西。
一些最后的注意事项:当你使用类似的东西时:
`somecommand`
Run Code Online (Sandbox Code Playgroud)
反引号并没有像您认为的那样做,实际上它们具有潜在危险。他们所做的是执行命令,捕获其输出,并尝试将该输出作为另一个命令执行。如果该命令不打印任何内容,则无所谓;但是如果它确实打印了一些东西,那么输出就不太可能是一个有效的命令。只是失去反引号。
最后,将密码作为命令参数提供是不安全的——在进程表中发布的命令参数(例如,ps命令可以看到它们),并且在公共场所发布密码是一个非常糟糕的主意。freebcp似乎没有任何替代方法可以做到这一点,但我找到了一个补丁,可以让它从标准输入中读取密码(echo "$password" | freebcp -P - ...-- 注意这echo是一个内置的 shell,所以它的参数不会出现在进程表中)。我对补丁的正确性、安全性等不做任何声明(尤其是因为它很旧),但如果我是你,我会检查一下。