Reputation: 11719
Creating symlinks on Ubuntu via FileUtils.symlink 'X', 'Y'
works while on Windows the following error occurs:
C:/ruby/lib/ruby/2.0.0/fileutils.rb:349:in `symlink': symlink() function is unimplemented on this machine (NotImplementedError)
from C:/ruby/lib/ruby/2.0.0/fileutils.rb:349:in `block in ln_s'
from C:/ruby/lib/ruby/2.0.0/fileutils.rb:1567:in `fu_each_src_dest0'
from C:/ruby/lib/ruby/2.0.0/fileutils.rb:347:in `ln_s'
from C:/test/test.rb:7:in `<class:Test>'
from C:/test/test.rb:1:in `<main>'
As a workaround it is possible to create symlinks on Windows via mklink
, but I would like to create symlinks on Windows via Ruby as well.
Upvotes: 1
Views: 2752
Reputation: 336
Just tested this with JRuby 9.1.13.0 (2.3.3) and FileUtils
seems to handle Windows symlinks now. The following is working if run as administrator. Softlinks should also work without administrator privileges.
require 'fileutils'
# Hardlinks work only for files
FileUtils.ln('./dir/file', './link')
# Softlinks to directories are created as symlinkd
FileUtils.ln_s('./dir', './link_dir')
Upvotes: 1
Reputation: 1263
First you need to run the script in elevated mode: Run ruby script in elevated mode
def windows?
RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
end
if windows?
def FileUtils.symlink source, dest, out = nil
dest = File.expand_path(dest).gsub(?/, ?\\)
source = File.expand_path(source).gsub(?/, ?\\)
opt = File.directory?(source) ? '/d ' : ''
output = `cmd.exe /c mklink #{opt}"#{dest}" "#{source}"` # & pause`
out.puts output if out
end
end
Upvotes: 1
Reputation: 495
Slightly embarrassed to post this ugly hack, but if you just GOTTA make it work, you can redefine methods and shell out to mklink. mklink is a bit complicated because it isn't a stand-alone program. Mklink is a cmd.exe shell command.
Below is for File class. For FileUtils, you can replace the class name and redefine methods you need as listed in the docs
require 'open3'
class << File
alias_method :old_symlink, :symlink
alias_method :old_symlink?, :symlink?
def symlink(old_name, new_name)
#if on windows, call mklink, else self.symlink
if RUBY_PLATFORM =~ /mswin32|cygwin|mingw|bccwin/
#windows mklink syntax is reverse of unix ln -s
#windows mklink is built into cmd.exe
#vulnerable to command injection, but okay because this is a hack to make a cli tool work.
stdin, stdout, stderr, wait_thr = Open3.popen3('cmd.exe', "/c mklink #{new_name} #{old_name}")
wait_thr.value.exitstatus
else
self.old_symlink(old_name, new_name)
end
end
def symlink?(file_name)
#if on windows, call mklink, else self.symlink
if RUBY_PLATFORM =~ /mswin32|cygwin|mingw|bccwin/
#vulnerable to command injection because calling with cmd.exe with /c?
stdin, stdout, stderr, wait_thr = Open3.popen3("cmd.exe /c dir #{file_name} | find \"SYMLINK\"")
wait_thr.value.exitstatus
else
self.old_symlink?(file_name)
end
end
end
Upvotes: 5