我有一个R脚本,当我从R Studio执行它时,它可以完美运行,但是当我尝试使用调用Shell的VBA代码运行它时,该代码运行,命令窗口显示并关闭,但是不生成结果文件。它不会引发任何错误。有人可以看到问题所在吗?
这是具有Rscript.exe
文件的文件夹的地址:C:\Program Files\R\R-3.4.4\bin\x64\
VBA代码:
Sub RunRScript()
Dim shell As Object
Dim waitTillComplete As Boolean: waitTillComplete = True
Dim style As Integer: style = 1
Dim errorCode As Integer
Dim path As String
Set shell = VBA.CreateObject("WScript.Shell")
path = """C:\Program Files\R\R-3.4.4\bin\x64\RScript"" C:\Ibos\R\WF_Metrics\abc.R"
errorCode = shell.Run(path, style, waitTillComplete)
End Sub
R脚本:
library(RODBC)
library(dplyr)
#library(data.table)
library(tidyr)
library(tictoc)
library(tidyverse)
library(lubridate)
library(xlsx)
library(sqldf)
#set working directory
setwd("C:/Ibos/R/WF_Metrics")
my_server="servername"
my_db="dbname"
my_username="username"
my_pwd="password"
db <- odbcDriverConnect(paste0("DRIVER={SQL Server};
server=",my_server,";
database=",my_db,";
uid=",my_username,";
pwd=",my_pwd))
sql="select * from dbo.metricsfinal"
df <- sqlQuery(db,sql)
myfile="results"
write.csv(df, file = paste0(myfile,"_test",".csv") ,row.names=FALSE)
编辑:
在奥利弗(Oliver)的回答和其他人的一些有益评论之后,我发现问题出在xlsx
软件包中。现在,我需要弄清楚如何解决此问题。我更喜欢使用此程序包,而不是寻找其他程序包/选项,不胜感激。这是错误:
错误:“ xlsx”的包或名称空间加载失败:.onLoad失败 用于'rJava'的loadNamespace(),详细信息:调用: dirname(this $ RuntimeLib)错误:字符向量参数 预期的执行停止
答案 0 :(得分:1)
从VBA运行R是一项令人讨厌的工作。如果您想直接从VBA运行R代码,我建议您研究BERT,它是Excel的开源加载项,可让您直接从excel运行和编写R代码,从而大大调试了代码更简单。
这就是说,如果您陷在shell.run中,可以做一些事情来定位问题。
在VBA中,设置断点或将您的路径打印到控制台。
path = """C:\Program Files\R\R-3.4.4\bin\x64\RScript"" C:\Ibos\R\WF_Metrics\abc.R"
debug.print path
打开命令提示符(按Windows按钮并输入cmd,然后按Enter start, cmd, enter
。)
将代码行粘贴到命令提示符中。任何错误都将被打印到命令行中,然后您可以在脚本中找到并更正错误。
现在,手动调试可能很乏味,正如我在下面指出的,它是特定于系统的。有几个选项可以使过程稍微自动化。这些包括:
第一个选项比较复杂,但是也非常通用且不错。我建议使用the first answer here,它提供了可以实现此方法的VBA模块的链接。但是请注意,它是一个32位模块,并且需要一些ptrsafe
标记,Windows api才能在Excel的64位安装上运行。只需进行一些更改,它甚至可以用于直接读取R中的文本输出(data.frame等),而对Excel的干扰最小。
对于第二个选项,我建议查看BERT网页,该网页为使用该实现提供了很好的指南。这样做的缺点是,除了要安装R以外,任何计算机都需要安装BERT才能使excel脚本正常工作。
第三个选项是受Chip Pearson's site.启发的选项,当脚本崩溃时,它会将错误代码发送到命令行,并且Windows错误代码管理器可以解释该错误代码。这具有简单的优点,并且您会很快知道脚本“不存在”还是非R特定的类似常见错误。
使用此方法可以将R执行脚本更改为类似
Sub RunRScript()
Dim shell As Object
Dim waitTillComplete As Boolean: waitTillComplete = True
Dim style As Integer: style = 1
Dim errorCode As Integer
Dim path As String
Set shell = VBA.CreateObject("WScript.Shell")
path = """C:\Program Files\R\R-3.4.4\bin\x64\RScript"" C:\Ibos\R\WF_Metrics\abc.R"
errorCode = shell.Run(path, style, waitTillComplete)
if errorCode <> 0 then
errorString = GetSystemErrorMessageText(errorCode)
Err.Raise errorCode, "Run_R_Script", errorString
end if
End Sub
其中GetSystemErrorMessageText(errorCode)
是对下面单独模块中的函数的调用。
#If Win64 Then
Private Declare PtrSafe Function FormatMessage Lib "kernel32" Alias "FormatMessageA" ( _
ByVal dwFlags As Long, _
ByVal lpSource As Any, _
ByVal dwMessageId As Long, _
ByVal dwLanguageId As Long, _
ByVal lpBuffer As String, _
ByVal nSize As Long, _
ByRef Arguments As Long) As Long
#Else
Private Declare Function FormatMessage Lib "kernel32" Alias "FormatMessageA" ( _
ByVal dwFlags As Long, _
ByVal lpSource As Any, _
ByVal dwMessageId As Long, _
ByVal dwLanguageId As Long, _
ByVal lpBuffer As String, _
ByVal nSize As Long, _
ByRef Arguments As Long) As Long
#End If
Public Function GetSystemErrorMessageText(ErrorNumber As Long) As String
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' GetSystemErrorMessageText
'
' This function gets the system error message text that corresponds
' to the error code parameter ErrorNumber. This value is the value returned
' by Err.LastDLLError or by GetLastError, or occasionally as the returned
' result of a Windows API function.
'
' These are NOT the error numbers returned by Err.Number (for these
' errors, use Err.Description to get the description of the error).
'
' In general, you should use Err.LastDllError rather than GetLastError
' because under some circumstances the value of GetLastError will be
' reset to 0 before the value is returned to VBA. Err.LastDllError will
' always reliably return the last error number raised in an API function.
'
' The function returns vbNullString is an error occurred or if there is
' no error text for the specified error number.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim ErrorText As String
Dim TextLen As Long
Dim FormatMessageResult As Long
Dim LangID As Long
''''''''''''''''''''''''''''''''
' Initialize the variables
''''''''''''''''''''''''''''''''
LangID = 0& ' Default language
ErrorText = String$(FORMAT_MESSAGE_TEXT_LEN, vbNullChar)
TextLen = FORMAT_MESSAGE_TEXT_LEN
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Call FormatMessage to get the text of the error message text
' associated with ErrorNumber.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
FormatMessageResult = FormatMessage( _
dwFlags:=FORMAT_MESSAGE_FROM_SYSTEM Or _
FORMAT_MESSAGE_IGNORE_INSERTS, _
lpSource:=0&, _
dwMessageId:=ErrorNumber, _
dwLanguageId:=LangID, _
lpBuffer:=ErrorText, _
nSize:=TextLen, _
Arguments:=0&)
If FormatMessageResult = 0& Then
''''''''''''''''''''''''''''''''''''''''''''''''''
' An error occured. Display the error number, but
' don't call GetSystemErrorMessageText to get the
' text, which would likely cause the error again,
' getting us into a loop.
''''''''''''''''''''''''''''''''''''''''''''''''''
MsgBox "An error occurred with the FormatMessage" & _
" API function call." & vbCrLf & _
"Error: " & CStr(Err.LastDllError) & _
" Hex(" & Hex(Err.LastDllError) & ")."
GetSystemErrorMessageText = "An internal system error occurred with the" & vbCrLf & _
"FormatMessage API function: " & CStr(Err.LastDllError) & ". No futher information" & vbCrLf & _
"is available."
Exit Function
End If
''''''''''''''''''''''''''''''''''''''''''''''''''''''
' If FormatMessageResult is not zero, it is the number
' of characters placed in the ErrorText variable.
' Take the left FormatMessageResult characters and
' return that text.
''''''''''''''''''''''''''''''''''''''''''''''''''''''
ErrorText = Left$(ErrorText, FormatMessageResult)
'''''''''''''''''''''''''''''''''''''''''''''
' Get rid of the trailing vbCrLf, if present.
'''''''''''''''''''''''''''''''''''''''''''''
If Len(ErrorText) >= 2 Then
If Right$(ErrorText, 2) = vbCrLf Then
ErrorText = Left$(ErrorText, Len(ErrorText) - 2)
End If
End If
''''''''''''''''''''''''''''''''
' Return the error text as the
' result.
''''''''''''''''''''''''''''''''
GetSystemErrorMessageText = ErrorText
End Function
贷记项转到Chip Pearson,尽管它可能不打算用于此用途。
现在执行shell.run(path)
时,path是执行R脚本的命令,如果失败,则将返回错误消息。
这并不能完全删除手动调试,但是错误消息将帮助您识别R之外的错误,并经常提供有价值的错误描述,以帮助您在手动调试期间更快地识别错误。
因此,应在必要时手动调试此方法。
Invalid function
或类似的代码。无法保证此错误将完全正确。但是,它确实会给您带来最常见的错误,例如错误的路径,功能根本不起作用,所调用的变量不存在等。我希望这可以使您有所了解并帮助您找到错误。我建议在google上搜索,使用shell.run
进行各种集成的主题已经过调查,如果存在更好的选择,通常建议避免使用它(由于限制)。但是,它通常是一个很好的起点。许多站点like this one展示了如何通过将输出保存到R中的文本文件并从VBA中读取来使用VBA中R中的输出。确实存在更好的选项,但这可能是最简单的方法。
由Asker更新: 经过大量调查,我意识到以下几点:
1- xlsx
软件包使用rJava
软件包,您需要先安装Java
2- Java的32位或64位版本会影响rJava
软件包以及随后的xlsx
软件包能否成功加载。如果您使用的是64位Windows操作系统,则建议安装所有内容(Excel,R,Java)的64位版本。
3-似乎默认情况下R已安装32位和64位版本,因此您必须指定要使用的版本。在RStudio上选择版本:
Tools > Global Options > R Version
默认情况下,
是32位的,并且在我安装了Java的新版本后,Library(rJava)甚至在RStudio上也会引发错误。我将其更改为64位,并且可以正常工作。
您也可以使用R脚本来执行此操作,但是,如果您从RStudion内部运行此代码,该代码将无法正常工作,因为您需要手动更改R版本并关闭RStudion并再次启动才能生效>
Sys.setenv(JAVA_HOME='C:\\Program Files\\Java\\jre7') # for 64-bit version
Sys.setenv(JAVA_HOME='C:\\Program Files (x86)\\Java\\jre7') # for 32-bit version
library(rJava)
4-我注意到我的机器上同时安装了32位和64位Java。对环境变量执行JAVA_HOME后,它导致了jvm.dll missing error
,因此我删除了它,一切又恢复正常了
5-我在一些帖子中看到,如果您对xlsx
软件包使用另一种替代方法,则无需费劲确保所有功能都可以正常工作。