ksh88 中是否有 Here-Strings?

Pra*_*kar 6 ksh here-string

我想从 ksh 脚本中 3 个不同变量的变量中提取 3 个单词。我在 ksh93 上使用了这条线,效果很好:

read A B C <<< $line

在 ksh88 上运行上述命令时出现此错误:

syntax error at line ## : '<' unexpected

如何在 ksh88 上执行相同的操作?

mir*_*los 13

不,这里的字符串在ksh88和上不可用pdksh。但是,在最近的ksh93(原始 AT&T Korn Shell)和mksh(当前积极开发的pdksh衍生产品)上,它是可用的。

<<<ksh93, mksh, GNUbashzsh.

您的具体问题...

read A B C <<< $line
Run Code Online (Sandbox Code Playgroud)

...可以解决这个问题(Korn shell):

print -r -- $line |&
read -p A B C
Run Code Online (Sandbox Code Playgroud)

你也可以使用这个(POSIX shell),虽然它有 tmpfile 性能损失(另一方面,<<<可能也有):

read A B C <<EOF
$line
EOF
Run Code Online (Sandbox Code Playgroud)

但是,如果您只想拆分单词:

set -A arrname -- $line
Run Code Online (Sandbox Code Playgroud)

然后使用${arrname[0]}代替$A${arrname[1]}代替$B。只有它不会停止在三个元素处进行拆分,因此如果$line是“ foo bar baz bla”,$C将包含“baz bla”,而${arrname[2]}具有“baz”并${arrname[3]}具有“bla”。

但是,如果您不需要位置参数,则可以执行

set -- $line
A=$1; shift
B=$1; shift
C=$*
Run Code Online (Sandbox Code Playgroud)

但是,shift如果$line少于三个单词,则会导致错误(检查$#您是否不确定,或者使用[[ $line = *' '*' '[! ] ]](虽然可能较慢)先检查)。

Mind that set … $linewill also do globbing(感谢 Stéphane 提醒我们),所以你需要set -o noglob之前(并且可能在之后恢复之前的状态,通常是set +o noglob)。

完全披露:我是mksh开发人员。

  • `set -- $line` 除了拆分之外还进行 globbing。使用 `set -o noglob` 来禁用它。 (4认同)

Sté*_*las 8

不,here-strings 来自zsh1991 年的 2.0(和/或 Unix 端口rc,他们各自的作者在那个时候交换了想法,不清楚两者中的哪一个有这个想法或首先将它包含在他的 shell 中)。

它在 2.05b (2002) 中被添加到 bash、m+ (2002) 中的 ksh93、R33 (2008) 中的 mksh、2.7 (2009) 中的 yash。

ksh88 没有获得任何新功能。

这里的文档 ( <<) 本身来自 70 年代后期的 Bourne-shell。

read A B C以一种非常特殊的方式将一个逻辑行读入A,BC变量(有点不那么特殊,它的默认值$IFS只包含 IFS 空白字符¹),反斜杠充当转义和行继续符以及将要发生的事情C也相当复杂。在没有的情况下做同样的事情read会很困难。

无论如何,这里<<<与 相同<<,仅在语法上有所不同。

read A B C << EOF
$var
EOF
Run Code Online (Sandbox Code Playgroud)

完全一样

read A B C <<< "$var" # note that some versions of bash need the quotes
Run Code Online (Sandbox Code Playgroud)

在所有贝壳中。对于这两种情况,shell 创建一个已删除的临时文件,其中包含内容和一个额外的换行符(尽管有些 shell 使用管道代替)。然后read从中读取一个逻辑行(可能在多条物理行上以反斜杠继续)并使用其复杂规则填充变量。

它仅分配第一3个字(SPC /制表符分隔)的$var$A$B并且 $C如果

  • $IFS 仍然包含其默认值
  • 变量不包含反斜杠和换行符
  • 该变量仅包含 3 个单词。

对于前三个词,你可以这样做:

function split_into {
  typeset words IFS v i=0
  set -o noglob

  set -A words -- $1; shift
  for v do
    eval "$v=\${words[i]}"
    ((i += 1))
  done
}

split_into "$var" A B C
  
  
Run Code Online (Sandbox Code Playgroud)

¹ IFS 空白字符,每个 POSIX 是[:space:]在语言环境中分类的字符,$IFS尽管在 ksh88(POSIX 规范所基于的)和大多数 shell 中,这仍然仅限于 SPC、TAB 和 NL。我发现在这方面唯一符合 POSIX 的 shell 是yash. ksh93并且bash(从 5.0 开始)还包括其他空格(例如 CR、FF、VT...),但仅限于单字节(在某些系统上(例如 Solaris)要小心,其中包括单字节的不间断空格在某些地区)