这里的罪魁祸首是shell,而不是R。
您没有指明您使用的是什么 shell,但它们 (sh/bash/ash/dash/ksh/zsh/tcsh) 的行为与我们的目的非常相似,因此假设您使用的是 bash,所以我可以引用一半的bash man page:
...
定义
本文档的其余部分使用以下定义。
...
name 仅由字母数字字符和下划线组成并以字母字符或下划线开头的单词。也称为标识符。
...
报价
...
用双引号括起来的字符会保留引号内所有字符的字面值,但 $、`、\ 以及启用历史扩展时的 ! 除外。字符 $ 和 ` 在双引号中保留其特殊含义。
...
参数
参数是存储值的实体。它可以是名称、数字或下面特殊参数下列出的特殊字符之一。变量是由名称表示的参数。
...
参数扩展
`$' 字符引入了参数扩展、命令替换或算术扩展。要扩展的参数名称或符号可以用大括号括起来,这是可选的,但用于保护要扩展的变量免受紧随其后的字符的影响,这些字符可以解释为名称的一部分。
...
${参数}
参数的值被替换。当参数是一个多于一位的位置参数时,或者当参数后跟一个不被解释为其名称的一部分的字符时,大括号是必需的。该参数是上面描述的shell参数(PARAMETERS)或数组引用(Arrays)。
...
因此,参数扩展在双引号字符串中生效,并且双引号字符串中有一个未转义的美元,后跟一个有效的变量名:$car。
我推断变量 $car 在您运行有问题的命令的 shell 会话中未设置。
不幸的是,shell 的默认行为是将未设置的变量默默地扩展为空字符串。因此,您的 R 代码最终会变成这样:
df = mtcars; df = rownames(mtcars); dim(df)
因此,df 被无量纲字符向量覆盖,dim(df) 在此类向量上返回 NULL。
问题可以通过反斜杠转义美元来解决:
Rscript -e "df <- mtcars; df\$car <- rownames(mtcars); dim(df);";
## [1] 32 12
或者更好的是,使用单引号,它不允许任何插值发生在单引号字符串中:
Rscript -e 'df <- mtcars; df$car <- rownames(mtcars); dim(df);';
## [1] 32 12
就我个人而言,IMO 似乎很少有工程师了解严格在软件设计中的重要性。如果 shell 从一开始就被设计为拒绝未设置的变量,这对计算机编程专业来说将是一项伟大的服务,但可惜,事实并非如此。所以像你我这样的可怜人,基本上所有做过 shell 编程的人都会不时遇到这些无声的错误。
幸运的是,早期的 shell 中添加了一个可选功能,称为 nounset:
help set| grep -e nounset -e -u;
## nounset same as -u
## -u Treat unset variables as an error when substituting.
如果你打开它,我强烈建议你这样做,那么有问题的 Rscript 命令将立即且明显地失败:
set -u;
Rscript -e "df <- mtcars; df$car <- rownames(mtcars); dim(df);";
## -bash: car: unbound variable