Linux下Java剪貼簿的訪問

xcghvgshjdfghsd發表於2022-05-12

1 概述

本文主要講述瞭如何利用xselLinux環境下對系統剪貼簿的訪問。

2 起因

在搜尋引擎直接搜尋“Java訪問剪貼簿”,大部分都是直接使用AWT API進行訪問的例子:

Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
StringSelection selection = new StringSelection("test");
clipboard.setContents(selection, selection);

但是,一個最大的問題是,需要該程式一直執行,才能訪問到剪貼簿,因此,如果沒有其他處理邏輯,需要加上執行緒休眠程式碼:

Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
StringSelection selection = new StringSelection("test");
clipboard.setContents(selection, selection);
TimeUnit.HOURS.sleep(1);

換句話說,這樣只是臨時複製到剪貼簿,並沒有永久複製,那麼,有沒有其他辦法可以在執行程式結束之後也能訪問到剪貼簿呢?

3 xsel

xselLinux下訪問剪貼簿的命令列工具,類似的還有xclip,沒有安裝的可以使用包管理器安裝。其中寫入到剪貼簿命令如下:

echo "test clipboard" | xsel -ib

由此想到了可以嘗試使用Runtime

public static void main(String[] args) throws Exception {
    Runtime runtime = Runtime.getRuntime();
    // 直接執行命令
    Process process = runtime.exec("echo \"111\" | xsel -ib");
    // 等待執行結束
    process.waitFor();
    StringBuilder builder = new StringBuilder();
    // 獲取輸出
    BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
    for (String s; ((s = reader.readLine()) != null); ) {
        builder.append(s);
    }
    System.out.println(builder);
    builder = new StringBuilder();
    // 獲取錯誤輸出
    reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
    for (String s; ((s = reader.readLine()) != null); ) {
        builder.append(s);
    }
    System.out.println(builder);
    // 獲取返回值
    int exitValue = process.exitValue();
    System.out.println("exitValue is " + exitValue);
    if (exitValue != 0) {
        System.out.println("error");
    }
    process.destroy();
}

執行之後輸出如下:

"111" | xsel -ib
"111" | xsel -ib
exitValue is 0

可以看到輸出結果是不正常的,這樣就相當於變成了執行

echo "\"111\" | xsel -ib"

也就是輸出的字串都是echo的引數。

4 建立指令碼檔案

出現上面結果的原因是Process並不能直接支援使用管道運算子,因此,採用直接建立指令碼執行命令的方法。

步驟:

  • 建立臨時指令碼檔案:使用Files.createFile建立
  • 授權:700許可權,也就是所有者讀、寫、執行許可權,使用Files.setPosixFilePermissions
  • 寫入指令碼檔案:向指令碼檔案寫入echo str | xsel -ib,使用Files.writeString
  • 執行:利用Process.exec執行指令碼檔案
  • 刪除:利用Files.delete刪除臨時檔案

程式碼如下:

public static void main(String[] args) throws Exception {
    String fileName = "1.sh";
    Path executeFile = Files.createFile(Path.of(fileName));
    Files.setPosixFilePermissions(executeFile, Set.of(PosixFilePermission.OWNER_WRITE,
            PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.OWNER_READ));
    String clipboardContent = "111";
    Files.writeString(executeFile, "echo " + clipboardContent + " | xsel -ib");
    Runtime runtime = Runtime.getRuntime();
    Process process = runtime.exec("./" + fileName);
    process.waitFor();
    StringBuilder builder = new StringBuilder();
    BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
    for (String s; ((s = reader.readLine()) != null); ) {
        builder.append(s);
    }
    System.out.println(builder);
    builder = new StringBuilder();
    reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
    for (String s; ((s = reader.readLine()) != null); ) {
        builder.append(s);
    }
    System.out.println(builder);
    int exitValue = process.exitValue();
    System.out.println("exitValue is " + exitValue);
    if (exitValue != 0) {
        System.out.println("error");
    }
    Files.delete(executeFile);
    process.destroy();
}

輸出:



exitValue is 0

有兩行空行是System.out.println()的換行輸出,說明ProcessinputStream以及errorStream都沒有內容。

測試結果也是正常,能夠剪貼出111字串。

5 從剪貼簿讀取

從剪貼簿讀取的原理類似,就是xsel的引數不一樣,這裡不展開了,放上完整程式碼:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Set;

public class Main {
    private static final String FILENAME = "1.sh";

    public static void main(String[] args) throws Exception {
        writeToClipboard("111111");
        System.out.println(readFromClipboard());
        writeToClipboard("22222");
        System.out.println(readFromClipboard());
    }

    //寫入到剪貼簿
    private static void writeToClipboard(String content) throws Exception {
        Path executeFile = createFile("echo " + content + " | xsel -ib");
        exec(executeFile);
    }

    //從剪貼簿讀取
    private static String readFromClipboard() throws Exception {
        Path executeFile = createFile("xsel -ob");
        return exec(executeFile);
    }

    private static Path createFile(String fileContent) throws Exception {
        Path executeFile = Files.createFile(Path.of(FILENAME));
        Files.setPosixFilePermissions(executeFile, Set.of(PosixFilePermission.OWNER_WRITE,
                PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.OWNER_READ));
        Files.writeString(executeFile, fileContent);
        return executeFile;
    }

    private static String exec(Path executeFile) throws Exception {
        Runtime runtime = Runtime.getRuntime();
        Process process = runtime.exec("./" + FILENAME);
        process.waitFor();
        StringBuilder builder = new StringBuilder();
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        for (String s; ((s = reader.readLine()) != null); ) {
            builder.append(s);
        }
        String res = "";
        if (builder.length() != 0) {
            res = builder.toString();
        }
        reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
        builder = new StringBuilder();
        for (String s; ((s = reader.readLine()) != null); ) {
            builder.append(s);
        }
        if (builder.length() != 0) {
            System.out.println(builder);
        }
        int exitValue = process.exitValue();
        System.out.println("exitValue is " + exitValue);
        if (exitValue != 0) {
            System.out.println("error");
        }
        Files.delete(executeFile);
        process.destroy();
        return res;
    }
}

相關文章