我想在 bash 脚本中运行这样的命令:
freebcp <authentication and other parameters> -t "," -r "\r\n"
当直接在命令行上运行时,该命令将按预期工作。但是当放入 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
当命令放置在变量中并且双引号被转义时:
cmd="freebcp ${db_name}.dbo.${table_name} in ${p_file} -S ${server_name} -U ${username} -P ${password} -t \",\" -r \"\r\n\" -c"
`$cmd`
笔记:将以下内容放入脚本中可以按预期工作:
`freebcp ${db_name}.dbo.${table_name} in ${p_file} -S ${server_name} -U ${username} -P ${password} -t "," -r "\r\n" -c`
所以我知道存在一些引用/转义/扩展问题,但我不知道如何解决它。
笔记2: 单引号 -t -r 参数也不起作用
答案1
简短回答:参见BashFAQ #50:我试图将命令放入变量中,但复杂的情况总是失败!。
长答案:shell 在解析命令行的过程中进行变量扩展——特别是在处理引号和转义之后。因此,在变量中添加引号和转义符与直接在命令行中添加引号和转义符的作用不同。
您的答案中的解决方案(加倍转义字符)将起作用(在大多数情况下),但不是因为您认为它起作用的原因,这让我相当紧张。命令:
cmd="freebcp ... -t "," -r "\\r\\n" -c"
被解析为双引号 string freebcp ... -t
,后跟不带引号的字符串,,
后跟双引号 string -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
请注意,我使用了双引号全部变量引用的数量——这通常是良好的脚本卫生,以防它们中的任何一个包含空格、通配符或 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[@]}"
再次注意双引号的大量使用;在这里,它们用于确保正确定义数组元素,而不是作为数组中存储的值的一部分。另请注意,数组并非在所有 shell 中都可用;确保您使用的是 bash 或 zsh 或类似的东西。
最后几点注意事项:当您使用以下内容时:
`somecommand`
反引号并没有像你想象的那样做,事实上它们有潜在的危险。他们所做的就是执行命令,捕获其输出,并尝试将该输出执行为其他命令。如果该命令没有打印任何内容,这并不重要;但如果它确实打印了一些内容,那么输出不太可能是有效的命令。只是失去反引号。
最后,将密码作为命令参数是不安全的——在进程表中发布命令参数(例如,命令ps
可以看到它们),并且在公共位置发布密码是一个非常糟糕的主意。freebcp
似乎没有任何替代方法可以做到这一点,但我找到了一个补丁这将让它从标准输入读取密码(echo "$password" | freebcp -P - ...
--注意这echo
是一个 shell 内置命令,所以它的参数不会出现在进程表中)。我对补丁的正确性、安全性等不做任何声明(特别是因为它相当旧),但如果我是你,我会检查一下。
答案2
转义反斜杠\n\r似乎已经成功了
cmd="freebcp ${db_name}.dbo.${table_name} in ${p_file} -S ${server_name} -U ${username} -P ${password} -t "," -r "\\r\\n" -c"