How copy file to clipboard using python or CL to paste it using STRG+V later on?
Asked Answered
T

2

6

I am trying to copy (using python or a CL command which I then can call using python) a file to the clipboard to later paste it using STRG+V. As far as I understand it, files are not "moved" into the clipboard, but rather the clipboard holds the path and an argument/flag that tells the OS "this is a file". I am happy with a linux-specific answer, but a universal answer would be the cherry on top.

pyperclip

Is not a solution, because it doesn't allow to copy files, just strings.

xclip

Is not a solution, because it only copies text

xclip-copyfile

Is not a solution, because it only copies to the X clipboard, not the clipboard. While xclip offers the option -selection clipboard (but only copies text), xclip-copyfile has no such option.

Using find

find ${PWD} -name "*.pdf"| xclip -i -selection clipboard -t text/uri-list

is a command described here: https://askubuntu.com/questions/210413/what-is-the-command-line-equivalent-of-copying-a-file-to-clipboard#answer-210428

But I can't replicate copying files with it and therefore assume that it is not working for all files.

Tinct answered 4/8, 2022 at 10:15 Comment(0)
M
6

Configurations

The clipboard is part of the Window Management and not of the Linux operating system itself. Different configurations with different distributions behave differently and therefore require different variants. Meanwhile, Wayland is increasingly on the way to successively replace X, which means there are three configurations to consider:

  • Wayland only
  • Wayland together with XWayland (compatibility with non-adapted X software)
  • X

Sending clipboard content

When saving to the clipboard, the system first only informs the receiver that data is available for the clipboard. Only on request, the actual data is sent. The program that sends the content to the clipboard must therefore not be terminated before the data has been transferred. Depending on the environment/configuration, it is also possible that the content of the clipboard is deleted as soon as the program is terminated.

How then does the xclip program already mentioned in the question work? It seems to terminate immediately after being called. But on closer inspection it doesn't, because it performs a fork, so that it is still present in the background (easily verifiable by looking at the source code or the command ps).

Format

Furthermore, different environments require the content in different ways. For example GNOME requires the list of files to be copied with the special target x-special/gnome-copied-files and a special formatting of the content, e.g. copy\nfile:///etc/group for the GNOME file manager Nautilus to perform the copy operation correctly.

Under KDE, on the other hand, there is then only one URI list with the target text/uri-list.

Determining the environment

The following example program works for Linuxmint 20.2 Cinnamon, Ubuntu 22.04 with Gnome and Kubuntu 22.04 with KDE. Other distributions / configurations may require some customization. Here it is advisable to simply copy a file in the appropriate file manager and then look at the clipboard contents with a program and then make appropriate adaptions to the script.

Based on the environment variables XDG_CURRENT_DESKTOP and WAYLAND_DISPLAY the following program tries to determine the environments.

If it is Wayland, wl-copy is used, otherwise xclip is used. The target and the content formatting is adapted accordingly. With subprocess.Popen the tool is started and the content is sent to stdin of the tool.

As soon as this is done, the program exits. Both wl-copy and xclip then create a fork, ensuring that the data is present in the clipboard.

import os
import subprocess
import sys
from pathlib import Path

gnome_desktops = ['X-Cinnamon', 'XFCE']


def is_gnome(desktop):
    if desktop.endswith("GNOME") or desktop in gnome_desktops:
        return True
    return False


def target():
    current_desktop = os.environ['XDG_CURRENT_DESKTOP']
    if is_gnome(current_desktop):
        return 'x-special/gnome-copied-files'
    elif current_desktop == 'KDE':
        return 'text/uri-list'
    else:
        sys.exit(f'unsupported desktop {current_desktop}')


def base_copy_cmd():
    if 'WAYLAND_DISPLAY' in os.environ:
        return 'wl-copy'
    return 'xclip -i -selection clipboard'


def copy_clipboard_cmd():
    return f"{base_copy_cmd()} -t '{target()}'"


def content(files_to_copy):
    uris = '\n'.join([Path(f).as_uri() for f in files_to_copy])
    current_desktop = os.environ['XDG_CURRENT_DESKTOP']
    if is_gnome(current_desktop):
        return f"copy\n{uris}".encode("utf-8")
    return uris.encode("utf-8")


def copy_to_clipboard(files_to_copy):
    copy_process = subprocess.Popen(copy_clipboard_cmd(), shell=True, stdin=subprocess.PIPE)
    copy_process.stdin.write(content(files_to_copy))
    copy_process.stdin.close()
    copy_process.wait()


if __name__ == '__main__':
    files = ['/etc/hosts', '/etc/group']
    copy_to_clipboard(files)

As mentioned above for other environments simply copy a file in the native file manager and then inspect the current clipboard contents and make appropriate adjustments to the script.

Depending on the environment, xclip or wl-copy (install the package wl-clipboard with your package manager) must be there. Detailed information about wl-copy can be found here: https://github.com/bugaevc/wl-clipboard.

Inspect Clipboard

Finally, to be able to dump the current contents of the clipboard, here is a small script that does just that. So it is possible to see what other programs like the native file manager put into the clipboard. Usually many programs put several different representations targets of the same data into the clipboard.

import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk


def on_activate(app):
    win = Gtk.ApplicationWindow(application=app)
    win.set_title("GTK Clipboard Util")
    win.set_default_size(256, 192)
    btn = Gtk.Button(label="Dump Clipboard")
    btn.connect('clicked', dump)
    box = Gtk.VBox()
    win.add(box)
    box.add(btn)
    win.show_all()


def dump(button):
    cb_targets = []
    counter = 0

    def print_content(clipboard, data):
        print(data.get_data())
        print()
        print_next_target_and_content(clipboard)

    def print_next_target_and_content(clipboard):
        nonlocal counter
        if counter < len(cb_targets):
            target = cb_targets[counter]
            print(target)
            clipboard.request_contents(target, print_content)
            counter += 1

    def get_targets(clipboard, targets, n_targets):
        nonlocal counter
        nonlocal cb_targets
        counter = 0
        cb_targets = targets
        print_next_target_and_content(clipboard)

    gtk_clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
    gtk_clipboard.request_targets(get_targets)


if __name__ == '__main__':
    app = Gtk.Application(application_id='com.software7.clipboard.formats')
    app.connect('activate', on_activate)
    app.run(None)
Melba answered 16/8, 2022 at 9:29 Comment(5)
Thank you very much for your very helpful answer! I have updated the question. Since I am using Xubuntu your program currently doesn't do the trick for me, but the general idea works of course. Sadly I know of no clipboard inspector that shows the actual string. I tried clipman and diodon.Tinct
That being said: Adding current_desktop == "XFCE" behind X-Cinnamon did the trick.Tinct
Perfect, then XFCE also works with the target x-special/gnome-copied-files.Melba
I would still be glad if you can give an example for a program or way to inspect the current clipboard to see the "special target". This would be very helpful for others, having the same question.Tinct
I have added XFCE to the answer. I have a small C program for inspecting the clipboard. Since the question is tagged with Python, I created a small Python program that does just that and added it to the answer as well.Melba
D
-1

Based on my understanding, you want to copy file just like "ctrl + c" in os using python.

Your string will need to be correctly formatted according to windows and linux OS specification which requires you to handle many edge cases so it would be better if you let the OS do all the work

An easy way for windows is the following script

import subprocess, sys    
p = subprocess.Popen("powershell.exe -command \"Set-Clipboard -Path D:\copy_file.py\"", stdout=sys.stdout)
p.communicate()

For linux, I found the following command which you can run.

import os
os.system("xclip-copyfile /home/copy_file.py") #copies file to clipboard
Dandiprat answered 4/8, 2022 at 11:5 Comment(1)
in the tests I did xclip didn't copy like "ctrl + c". I was unable to paste with "ctrl + v" but was only able to see the copied data by using the -o flag. I therefore think this is not a solution. If I am wrong, please give additional information.Tinct

© 2022 - 2024 — McMap. All rights reserved.