posix
我在 Windows 上的 Git bash shell 中使用了一个函数,可以将 DOS 样式路径转换为正常的 Unix 样式路径。由于 DOS 样式路径使用反斜杠作为分隔符,因此我必须引用路径参数以防止 shell 使用反斜杠将下一个字符表示为文字。有什么办法可以得到未解释的来自我的函数内部的参数,这样我就不需要引用它?
这是我的功能,如果有帮助的话:
function posix() {
echo $1 | sed -e 's|\\|/|g' | sed -re 's|^(.)\:|/\L\1|'
}
(顺便说一句,我欢迎任何评论以及以与解决引用/shell 解释问题无关的其他方式改进功能的提示。)
答案1
未解释的 shell 参数有$1
、$2
等。在大多数情况下,您需要将它们的扩展放在双引号中,以避免参数的值进一步扩展。"$@"
为您提供所有参数的列表。
例如,如果您想将 shell 脚本的参数传递给您的函数,请像这样调用它:
first_argument_as_filename_in_unix_syntax=$(posix "$1")
双引号是必要的。如果您编写posix $1
,那么您传递的不是第一个参数的值,而是对第一个参数的值执行分词和通配的结果。调用脚本时您也需要使用正确的引用。例如,如果您在 bash 中编写以下内容:
myscript c:\path with spaces\somefile
那么实际的、未解释的参数将myscript
是c:path
、with
和spacessomefile
。所以不要这样做。
你的posix
函数是错误的,再次因为它缺少双引号$1
。始终在变量和命令替换两边加上双引号:"$foo"
, "$(foo)"
。记住这条规则比记住实际上不需要引号的例外情况更容易。
echo
在某些情况下会进行自己的处理,并且调用外部进程很慢(尤其是在 Windows 上)。您可以在 bash 中完成整个处理。
posix () {
path="${1//\\//}"
case "$path" in
?:*) drive="${p:0:1}"; drive="${drive,}"; p="/$drive/${p:2}";;
esac
printf %s "$p"
}
jw013 提到的 zsh 功能并没有像你想象的那样做。您可以放在noglob
命令前面,并且 zsh 不会对参数执行通配符(即文件名生成,即通配符扩展)。例如,在 zsh 中,如果您编写noglob locate *foo*bar*
,则locate
使用参数 进行调用*foo*bar*
。您通常会将noglob
内置函数隐藏在别名后面。此功能与您想要执行的操作无关。
答案2
虽然其他答案可能是正确的,指出您无法通过他们提到的方式接收“未解释的”shell 输入,但他们断然否认这种可能性是错误的。如果您指示 shell 不解释它,那么您当然可以在 shell 解释它之前收到它。简单的 POSIXheredoc
使这变得非常简单:
% sed -e 's@\\@/@g' -e 's@\(.\):\(.*\)@/drive/\1\2@' <<'_EOF_'
> c:\some\stupid\windows\place
> _EOF_
/drive/c/some/stupid/windows/place
编辑1:
为了将这样的字符串作为 shell 参数传递给 shell 函数,您需要将其存储在 shell 变量中。一般来说,你不能简单地var=<<'HEREDOC'
不幸,但 POSIX 确实指定了内置-r
参数read
:
% man read
POSIX 程序员手册
...
默认情况下,除非指定 -r 选项,否则反斜杠 ( '\' ) 应充当转义字符,如转义字符 (反斜杠) 中所述。如果标准输入是终端设备并且调用 shell 是交互式的,则在以下情况下 read 将提示输入续行:
除非指定 -r 选项,否则 shell 读取以反斜杠结尾的输入行。
输入新行后,此处文档不会终止。
组合起来后,read
这heredoc
也变得微不足道且便于携带,尽管一开始可能感觉不太直观:
% _stupid_mspath_fix() {
> sed -e 's@\\@/@g' -e 's@\(.\):\(.*\)@/drive/\1\2@' <<_EOF_
>> ${1}
>> _EOF_
> }
% read -r _stupid_mspath_arg <<'_EOF_'
> c:\some\stupid\windows\place
> _EOF_
% _stupid_mspath_fix ${_stupid_mspath_arg}
/drive/c/some/stupid/windows/place
编辑2:
heredocs
您可能在第二个示例中注意到了两者之间的区别。heredoc
_EOF_
函数内的终止符不带引号,而输入的终止符则用read
单引号引起来。通过这种方式,shell 被指示使用heredoc
未加引号的终止符对 执行扩展,但当其终止符被加引号时则不执行此操作。当扩展函数中未加引号的内容时,它不会中断,heredoc
因为它扩展的变量的值已经设置为带引号的字符串,并且不会对其进行两次解析。
您可能想要做的是将 Windows 路径从一个命令的输出动态地传递到另一个命令的输入。 a 中的命令替换heredoc
使得这成为可能:
% _stupid_mspath_fix() {
> sed -e 's@\\@/@g' -e 's@\(.\):\(.*\)@/drive/\1\2@' <<_EOF_
>> ${1}
>> _EOF_
> }
% read -r _stupid_mspath_arg <<'_EOF_'
> c:\some\stupid\windows\place
> _EOF_
% _stupid_mspath_fix ${_stupid_mspath_arg}
/drive/c/some/stupid/windows/place
% read -r _second_stupid_mspath_arg <<_EOF_
> $(printf ${_stupid_mspath_arg})
> _EOF_
% _stupid_mspath_fix ${_second_stupid_mspath_arg}
/drive/c/some/stupid/windows/place
因此,基本上,如果您可以可靠地从某个应用程序输出反斜杠(我printf
在上面使用过),那么在内部运行该命令$(...)
并将其包含在传递heredoc
给另一个可以可靠地接受反斜杠作为输入的应用程序(例如上面的应用程序)的read
未加引号中sed
,将绕过shell 完全解析你的反斜杠。应用程序是否可以将反斜杠作为输入/输出处理,您必须自己找出答案。
与问题不严格相关:
在 Gilles 的回答中,他推荐了${var/search/replace}
参数扩展形式,虽然很酷,但不是 POSIX。这绝对是一种羞辱。这对我来说并不重要,但在他的编辑中,他保留了posix ()
函数名称,这可能会误导某些人。
在这一点上,原始帖子的posix ()
函数使用了非常方便的扩展正则sed -r
表达式参数,但不幸的是,这也不是 POSIX。 POSIX 没有为 指定扩展正则表达式参数sed
,因此它的使用可能不可靠。
我的账户在堆栈溢出也只有几天前,但我在那里发布了一些专门处理 POSIX 参数扩展的答案,您可以从我的个人资料页面找到链接,其中我引用了 POSIX 指南和链接到那里。您还会发现我在其中演示了 的其他用法heredoc
,例如将整个 shell 脚本读入 shell 变量,以编程方式解析和操作它,然后最终运行其新版本,所有这些都在另一个脚本或 shell 函数中完成。只是说。
答案3
你不能拥有 shell 进程”未解释的“输入。您在命令行中键入的只是一串字符,这些字符正是要由 shell 解释的。您不能让 shell 将您的文字、键入的字符传递给函数或命令,因为它有解释它们以了解要调用什么命令/函数以及使用什么参数!当您在命令提示符下键入内容时,您必须接受必须根据 shell 解释规则进行键入(因为您正在使用 shell!)。
shell 解释规则的选择非常有帮助。它们不会妨碍您,而是为您提供了一种指示 shell 执行您想要的任何操作的方法,包括指定任意参数。如果没有解释,shell 就无法从您的输入中提取要精确执行的操作。解释意味着特殊字符(如空格),如果不转义,就无法将这些字符传递给命令。
关于解释本身,我一直发现bash 文档这一点就非常好。简而言之,波浪号扩展之类的事情很早就发生(并且波浪号扩展仅在某些情况下发生)。然后,发生变量替换和分词,最后是通配符。