How to copy output of a function to your clipboard in Elixir or Ruby

Having the ability to drive your development using just a keyboard is very productive. However, when you are using a terminal and have to copy the output of a command to use it somewhere else, it breaks your flow, you need to move your hands away from your keyboard, use the mouse to select the text and then copy it.

When I want to copy passwords to be used elsewhere from my browser, I usually open the developer tools console, inspect element and click on the password input box and then run the following code:

1
copy($0.value)

Chrome sets $0 to refer to the currently selected DOM element and $0.value will give us the value of the password field and sending it to the copy function copies this text to the OS clipboard.

I have a similar script set up for my terminal, when I want to copy the output of a command like rake secret I run the following command:

1
2
rake secret | xc # copies a new secret to the clipboard.
echo "Hello" | xc # copies the string `Hello` to the clipboard.

xc is aliased to the following in my bashrc:

1
alias xc='tee /dev/tty | xclip -selection clipboard'

This command prints the output to the terminal (using tee /dev/tty) and copies it to the OS clipboard using the xclip package.

I wanted the same ability in my ruby and elixir REPLs. It was pretty straightforward to do in ruby. Here is the annotated code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
puts 'loading ~/.pryrc ...'

require 'open3'

# copy takes an argument and converts it into a string and copies it to the OS
# clipboard using the `xclip` command line package.
def copy(text)
# start running the `xclip` command to copy the stdin to the OS primary
# clipboard. Also pass the stdin and stdout, stderr to the block
Open3.popen3('xclip', '-selection', 'clipboard') do |stdin, _stdout, _stderr, _wait_thr|
# convert the input argument to a string and write it to the stdin of the
# spawned `xclip` process and the close the input stream
stdin.puts text.to_s
stdin.close
end

# print out an informational message to signal that the argument has been
# copied to the clipboard.
puts "copied to clipboard: #{text.to_s[0..10]}..."
end

# e.g. running `copy SecureRandom.uuid` will print the following
# pry(main)> copy SecureRandom.uuid
# copied to clipboard: 14438d5c-62...
# and copies: `14438d5c-62b9-40a1-a324-5d2bd2205990` to the OS clipboard

Below is a similar script for Elixir:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
IO.puts("loading ~/.iex.exs")

# open a module called `H` as we can't have functions outside modules
defmodule H do
# copy function takes the input and converts it into a string before copying
# it to the OS clipboard.
def copy(text) do
# convert input argument to a string
text = to_s(text)

# spawn a new xclip process configured to copy the stdin to the OS's primary
# clipboard
port = Port.open({:spawn, "xclip -selection clipboard"}, [])
# send the input text as stdin to the xclip process
Port.command(port, text)
# close the port
Port.close(port)

# print out an informational message to signal that the text has been copied
# to the OS's clipboard"
IO.puts("copied to clipboard: #{String.slice(text, 0, 10)}...")
end

# to_s converts an elixir term to a string if it implements the `String.Chars`
# protocol otherwise it uses `inspect` to convert it into a string.
defp to_s(text) do
to_string(text)
rescue
_ -> inspect(text)
end
end
1
2
3
iex(2)> :crypto.strong_rand_bytes(16) |> Base.encode16 |> H.copy
# copied to clipboard: 347B175C6F...
# it has also copied `347B175C6F397B2808DE7168444ED428` to the OS's clipboard

All these utilities (except for the browser’s copy function) depend on the xclip utility which can be installed on ubuntu using sudo apt-get install xclip. You can emulate the same behaviour on a Mac using the pbcopy utility, you might have to tweak things a little bit, but it should be pretty straightforward.

You can do the same in your favorite programming language too, just find the right way to spawn an xclip process and send the text you want to be copied to its’ stdin. Hope this makes your development a little more pleasant :)


I am currently working on LiveForm which makes setting up contact forms on your website a breeze.