使用 R 读取以 "smb://" 开头的远程文件
Read remote file beginning with "smb://" using R
要读取 R
中的文件,我通常会执行如下操作:
read.csv('/Users/myusername/myfilename.csv')
但是,我正在尝试读取位于远程服务器(Windows SMB/CIFS 共享)上的文件,我通过 在 Mac 上访问该文件Finder → Go → Connect to Server 菜单项。
当我查看那个文件的属性时,文件路径与我习惯的不同。不是以 /Users/myusername/...
开头,而是 smb://server.msu.edu/.../myfilename.csv
.
尝试读取文件,我尝试了以下操作:
read.csv('smb://server.msu.edu/.../myfilename.csv')
但是,这没有用。
而不是通常的 "No such file or directory" 错误,返回的是:
smb://server.msu.edu/.../myfilename.csv does not exist in current working directory
我想文件路径需要不同的格式,但我不知道是什么。
如何在 R
中读取这种类型的文件?
SMB 是 Windows 网络文件夹协议。
例如,类似的情况包括 sftp://
个网址。
您可以:
- 在您的操作系统中安装该文件夹,并使用常规路径访问它,
- 使用虚拟文件系统库,例如 GVFS/GIO on Linux。也许有一些 R 包装器可以使用。
下面我展示了一种我不时使用的从 SMB 网络驱动器读取数据的方法。在下面的代码中,我使用 R system
函数在 R 中执行所有操作,但您也可以从 OSX 命令行或在 Finder 中使用 Command-K(连接到服务器):
如果您还没有,请在共享所在的本地驱动器上创建一个目录(这不是必需的,因为您可以将驱动器安装在现有位置):
system("mkdir /Users/eipi10/temp_share/")
或
dir.create("/Users/eipi10/temp_share/")
将网络驱动器安装到您刚刚创建的文件夹中。在下面的代码中,//username@domain.address.edu/home/u/eipi10
是您的用户名和 SMB 共享的地址。
system("mount_smbfs //username@domain.address.edu/home/u/eipi10 /Users/eipi10/temp_share")
如果有密码认证,那么也可以加上密码:
system("mount_smbfs //username:password@domain.address.edu/home/u/eipi10 /Users/eipi10/temp_share")
读取数据:
dat = read.csv("/Users/eipi10/temp_share/fileToRead.csv")
在 R 中,您还可以通过编程方式 select 文件来读取:
data.list = lapply(list.files(pattern="csv$", "/Users/eipi10/temp_share/", full.names=TRUE), read.csv)
说明
smb://educ-srvmedia1.campusad.msu.edu/...
实际上是 URL 而不是文件路径。
我们来分解一下
smb://
表示使用server message block protocol(文件共享)
educ-srvmedia1.campusad.msu.edu
是服务器的名称
/.../myfilename.csv
是远程服务器
上的文件 share/path
您可以使用 OSX 上的 Finder 导航到此目录,因为它内置了对 SMB 协议的支持。 Finder 使用 URL 连接到远程服务并允许您浏览文件。
但是 R
不了解 SMB 协议,因此无法正确解释文件路径。
R
函数 read.csv()
在内部使用 file()
,参见 https://stat.ethz.ch/R-manual/R-devel/library/base/html/connections.html
url and file support URL schemes file://, http://, https:// and ftp://
因此 R returns "unable to locate the file" 消息因为无法找到文件,因为协议不受支持。是的,有点混乱。
修复
您需要在本地文件系统上安装文件共享。
这意味着 SMB 协议的细节将由 OS 在幕后处理,文件共享将作为本地目录显示。
这将允许 R(和其他程序)出于所有目的和目的处理远程文件,就像任何其他本地文件一样。
This discussion 显示了一些这样做的选项。
例如
# need to create /LocalFolder first
mount -t cifs //username:password@hostname/sharename /LocalFolder
然后在 R 中:
read.csv('/LocalFolder/myfilename.csv')
额外
Windows 用户可以使用 UNC 路径更轻松地完成此操作
How to read files from a UNC-specified directory in R?
在我看来,有两种方法可以实现您的目标。
第一个是使用fstab 将远程文件夹明确添加为本地磁盘
第二个是需要的时候临时挂载远程文件夹作为文件夹
下面,我将解释第二种方法是如何实现的
创建本地目录:
mkdir <mountdirectory>
使用此命令行将远程目录挂载到本地:
sshfs <remoteserverip>:<remotedirpath> <mountdirectory>
用于 SSH
或(先安装cifs util:sudo apt-get install cifs-utils
)
mount -t cifs -o username=<USERNAME>,password=<PASSWD> //<remoteserverip>/<remotedirpath> <mountdirectory>
对于 SMB
用本地文件完成工作 !
最后,使用这个命令卸载:
fusermount -u <mountdirectory>
TL;DR
这是一种使用 cURL 并且不需要安装远程文件系统的可移植方法:
> install.packages("curl")
> require("curl")
> handle <- new_handle()
> handle_setopt(handle, username = "domain\username")
> handle_setopt(handle, password = "secret") # If needed
> request <- curl_fetch_memory("smb://host.example.com/share/file.txt", handle = handle)
> contents <- rawToChar(request$content)
如果我们需要以 CSV 格式读取内容,就像问题中那样,我们可以通过另一个函数流式传输文件:
> stream <- curl("smb://host.example.com/share/file.txt", handle = handle)
> contents <- read.csv(stream)
除了其他答案中描述的安装远程文件系统的方法之外,让我们看看通过 smb:// URL 访问远程文件的更可靠的方法。不幸的是,我来晚了一点,但我希望这对未来的读者有所帮助。
在某些情况下,我们可能没有挂载文件系统所需的权限(在许多系统上这需要管理员或 root 访问权限),或者我们可能根本 不想 挂载整个文件系统只是为了读取单个文件。我们将使用 cURL library 来读取文件。这种方法提高了我们程序的灵活性和可移植性,因为我们不需要依赖外部挂载文件系统的存在。我们将研究两种不同的方法:通过 system()
调用,以及使用提供 cURL API.
的包
一些背景知识:对于那些不熟悉它的人,cURL 提供了用于通过各种协议传输数据的工具。自 7.40 版以来,cURL 支持通常用于 Windows 文件共享服务的 SMB/CIFS 协议。 cURL 包含 a command-line tool,我们可以使用它来获取文件的内容:
$ curl -u 'domain\username' 'smb://host.example.com/share/file.txt'
上面的命令从远程服务器host.example.com[=104读取并输出(到STDOUT)file.txt的内容=] 作为域上的指定用户进行身份验证。如果需要,该命令将提示我们输入密码。如果我们的网络不使用域,我们可以从用户名中删除域部分。
系统调用
我们可以使用 system()
function 在 R 中实现相同的功能:
system("curl -u 'domain\username' 'smb://host.example.com/share/file.txt'")
注意 domain\username
中的双反斜杠。这会转义反斜杠字符,以便 R 不会将其解释为字符串中的转义字符。我们可以通过将 system()
函数的 intern
参数设置为 TRUE
:
来将命令输出中的文件内容捕获到一个变量中
contents <- system("curl -u 'domain\username' 'smb://host.example.com/share/file.txt'", intern = TRUE)
...或者通过调用 system2()
来代替,它引用命令参数以确保安全并更好地处理平台之间的进程重定向:
contents <- system2('curl', c("-u", "domain\\username", "smb://host.example.com/share/file.txt"), stdout = TRUE)
如果远程服务器需要,curl 命令仍会提示我们输入密码。虽然我们 可以 使用 -u 'domain\username:password'
指定密码来避免提示,但这样做会在命令字符串中暴露明文密码。要获得更安全的方法,请阅读下面描述包用法的部分。
我们也可以在curl命令中添加-s
或--silent
标志来抑制进度状态输出。请注意,这样做也会隐藏错误消息,因此我们可能还想添加 -S
(--show-error
)。 contents
变量将包含文件行的向量——类似于 readLines("file.txt")
返回的值——我们可以使用 paste(contents, collapse = "\n")
.
将其压缩在一起
cURL API
虽然这一切都很好,但我们可以通过使用专用的 cURL 库来改进这种方法。这 curl package provides R bindings to libcurl 这样我们就可以在我们的程序中直接使用 cURL API 了。首先我们需要安装包:
install.packages("curl")
require("curl")
(Linux 用户需要 install libcurl development files。)
然后,我们可以使用curl_fetch_memory()
函数将远程文件读入一个变量:
handle <- new_handle()
handle_setopt(handle, username = "domain\username")
handle_setopt(handle, password = "secret") # If needed
request <- curl_fetch_memory("smb://host.example.com/share/file.txt", handle = handle)
content <- rawToChar(request$content)
首先,我们创建一个 handle
以通过设置所需的任何身份验证选项来配置请求。然后,我们执行请求并将文件的内容分配给一个变量。如图所示,根据需要设置 password
CURLOPT。
要像 read.csv()
一样处理远程文件,我们需要创建一个流连接。 curl()
函数创建一个连接对象,我们可以使用它通过任何支持标准 url()
函数返回的参数的函数来流式传输文件内容。例如,这里有一种将远程文件读取为 CSV 的方法,如问题:
handle = new_handle()
...
stream <- curl("smb://host.example.com/share/file.txt", handle = handle)
contents <- read.csv(stream)
当然,上述概念适用于通过 cURL 支持的任何协议 获取内容或响应主体,而不仅仅是 SMB/CIFS。如果需要,我们还可以使用这些工具将文件下载到文件系统,而不仅仅是将内容读入内存。
要读取 R
中的文件,我通常会执行如下操作:
read.csv('/Users/myusername/myfilename.csv')
但是,我正在尝试读取位于远程服务器(Windows SMB/CIFS 共享)上的文件,我通过 在 Mac 上访问该文件Finder → Go → Connect to Server 菜单项。
当我查看那个文件的属性时,文件路径与我习惯的不同。不是以 /Users/myusername/...
开头,而是 smb://server.msu.edu/.../myfilename.csv
.
尝试读取文件,我尝试了以下操作:
read.csv('smb://server.msu.edu/.../myfilename.csv')
但是,这没有用。
而不是通常的 "No such file or directory" 错误,返回的是:
smb://server.msu.edu/.../myfilename.csv does not exist in current working directory
我想文件路径需要不同的格式,但我不知道是什么。
如何在 R
中读取这种类型的文件?
SMB 是 Windows 网络文件夹协议。
例如,类似的情况包括 sftp://
个网址。
您可以:
- 在您的操作系统中安装该文件夹,并使用常规路径访问它,
- 使用虚拟文件系统库,例如 GVFS/GIO on Linux。也许有一些 R 包装器可以使用。
下面我展示了一种我不时使用的从 SMB 网络驱动器读取数据的方法。在下面的代码中,我使用 R system
函数在 R 中执行所有操作,但您也可以从 OSX 命令行或在 Finder 中使用 Command-K(连接到服务器):
如果您还没有,请在共享所在的本地驱动器上创建一个目录(这不是必需的,因为您可以将驱动器安装在现有位置):
system("mkdir /Users/eipi10/temp_share/")
或
dir.create("/Users/eipi10/temp_share/")
将网络驱动器安装到您刚刚创建的文件夹中。在下面的代码中,//username@domain.address.edu/home/u/eipi10
是您的用户名和 SMB 共享的地址。
system("mount_smbfs //username@domain.address.edu/home/u/eipi10 /Users/eipi10/temp_share")
如果有密码认证,那么也可以加上密码:
system("mount_smbfs //username:password@domain.address.edu/home/u/eipi10 /Users/eipi10/temp_share")
读取数据:
dat = read.csv("/Users/eipi10/temp_share/fileToRead.csv")
在 R 中,您还可以通过编程方式 select 文件来读取:
data.list = lapply(list.files(pattern="csv$", "/Users/eipi10/temp_share/", full.names=TRUE), read.csv)
说明
smb://educ-srvmedia1.campusad.msu.edu/...
实际上是 URL 而不是文件路径。
我们来分解一下
smb://
表示使用server message block protocol(文件共享)
educ-srvmedia1.campusad.msu.edu
是服务器的名称
/.../myfilename.csv
是远程服务器
您可以使用 OSX 上的 Finder 导航到此目录,因为它内置了对 SMB 协议的支持。 Finder 使用 URL 连接到远程服务并允许您浏览文件。
但是 R
不了解 SMB 协议,因此无法正确解释文件路径。
R
函数 read.csv()
在内部使用 file()
,参见 https://stat.ethz.ch/R-manual/R-devel/library/base/html/connections.html
url and file support URL schemes file://, http://, https:// and ftp://
因此 R returns "unable to locate the file" 消息因为无法找到文件,因为协议不受支持。是的,有点混乱。
修复
您需要在本地文件系统上安装文件共享。
这意味着 SMB 协议的细节将由 OS 在幕后处理,文件共享将作为本地目录显示。
这将允许 R(和其他程序)出于所有目的和目的处理远程文件,就像任何其他本地文件一样。 This discussion 显示了一些这样做的选项。
例如
# need to create /LocalFolder first
mount -t cifs //username:password@hostname/sharename /LocalFolder
然后在 R 中:
read.csv('/LocalFolder/myfilename.csv')
额外
Windows 用户可以使用 UNC 路径更轻松地完成此操作
How to read files from a UNC-specified directory in R?
在我看来,有两种方法可以实现您的目标。
第一个是使用fstab 将远程文件夹明确添加为本地磁盘
第二个是需要的时候临时挂载远程文件夹作为文件夹
下面,我将解释第二种方法是如何实现的
创建本地目录:
mkdir <mountdirectory>
使用此命令行将远程目录挂载到本地:
sshfs <remoteserverip>:<remotedirpath> <mountdirectory>
用于 SSH或(先安装cifs util:
sudo apt-get install cifs-utils
)mount -t cifs -o username=<USERNAME>,password=<PASSWD> //<remoteserverip>/<remotedirpath> <mountdirectory>
对于 SMB用本地文件完成工作 !
最后,使用这个命令卸载:
fusermount -u <mountdirectory>
TL;DR
这是一种使用 cURL 并且不需要安装远程文件系统的可移植方法:
> install.packages("curl")
> require("curl")
> handle <- new_handle()
> handle_setopt(handle, username = "domain\username")
> handle_setopt(handle, password = "secret") # If needed
> request <- curl_fetch_memory("smb://host.example.com/share/file.txt", handle = handle)
> contents <- rawToChar(request$content)
如果我们需要以 CSV 格式读取内容,就像问题中那样,我们可以通过另一个函数流式传输文件:
> stream <- curl("smb://host.example.com/share/file.txt", handle = handle)
> contents <- read.csv(stream)
除了其他答案中描述的安装远程文件系统的方法之外,让我们看看通过 smb:// URL 访问远程文件的更可靠的方法。不幸的是,我来晚了一点,但我希望这对未来的读者有所帮助。
在某些情况下,我们可能没有挂载文件系统所需的权限(在许多系统上这需要管理员或 root 访问权限),或者我们可能根本 不想 挂载整个文件系统只是为了读取单个文件。我们将使用 cURL library 来读取文件。这种方法提高了我们程序的灵活性和可移植性,因为我们不需要依赖外部挂载文件系统的存在。我们将研究两种不同的方法:通过 system()
调用,以及使用提供 cURL API.
一些背景知识:对于那些不熟悉它的人,cURL 提供了用于通过各种协议传输数据的工具。自 7.40 版以来,cURL 支持通常用于 Windows 文件共享服务的 SMB/CIFS 协议。 cURL 包含 a command-line tool,我们可以使用它来获取文件的内容:
$ curl -u 'domain\username' 'smb://host.example.com/share/file.txt'
上面的命令从远程服务器host.example.com[=104读取并输出(到STDOUT)file.txt的内容=] 作为域上的指定用户进行身份验证。如果需要,该命令将提示我们输入密码。如果我们的网络不使用域,我们可以从用户名中删除域部分。
系统调用
我们可以使用 system()
function 在 R 中实现相同的功能:
system("curl -u 'domain\username' 'smb://host.example.com/share/file.txt'")
注意 domain\username
中的双反斜杠。这会转义反斜杠字符,以便 R 不会将其解释为字符串中的转义字符。我们可以通过将 system()
函数的 intern
参数设置为 TRUE
:
contents <- system("curl -u 'domain\username' 'smb://host.example.com/share/file.txt'", intern = TRUE)
...或者通过调用 system2()
来代替,它引用命令参数以确保安全并更好地处理平台之间的进程重定向:
contents <- system2('curl', c("-u", "domain\\username", "smb://host.example.com/share/file.txt"), stdout = TRUE)
如果远程服务器需要,curl 命令仍会提示我们输入密码。虽然我们 可以 使用 -u 'domain\username:password'
指定密码来避免提示,但这样做会在命令字符串中暴露明文密码。要获得更安全的方法,请阅读下面描述包用法的部分。
我们也可以在curl命令中添加-s
或--silent
标志来抑制进度状态输出。请注意,这样做也会隐藏错误消息,因此我们可能还想添加 -S
(--show-error
)。 contents
变量将包含文件行的向量——类似于 readLines("file.txt")
返回的值——我们可以使用 paste(contents, collapse = "\n")
.
cURL API
虽然这一切都很好,但我们可以通过使用专用的 cURL 库来改进这种方法。这 curl package provides R bindings to libcurl 这样我们就可以在我们的程序中直接使用 cURL API 了。首先我们需要安装包:
install.packages("curl")
require("curl")
(Linux 用户需要 install libcurl development files。)
然后,我们可以使用curl_fetch_memory()
函数将远程文件读入一个变量:
handle <- new_handle()
handle_setopt(handle, username = "domain\username")
handle_setopt(handle, password = "secret") # If needed
request <- curl_fetch_memory("smb://host.example.com/share/file.txt", handle = handle)
content <- rawToChar(request$content)
首先,我们创建一个 handle
以通过设置所需的任何身份验证选项来配置请求。然后,我们执行请求并将文件的内容分配给一个变量。如图所示,根据需要设置 password
CURLOPT。
要像 read.csv()
一样处理远程文件,我们需要创建一个流连接。 curl()
函数创建一个连接对象,我们可以使用它通过任何支持标准 url()
函数返回的参数的函数来流式传输文件内容。例如,这里有一种将远程文件读取为 CSV 的方法,如问题:
handle = new_handle()
...
stream <- curl("smb://host.example.com/share/file.txt", handle = handle)
contents <- read.csv(stream)
当然,上述概念适用于通过 cURL 支持的任何协议 获取内容或响应主体,而不仅仅是 SMB/CIFS。如果需要,我们还可以使用这些工具将文件下载到文件系统,而不仅仅是将内容读入内存。