Skip to content

Instantly share code, notes, and snippets.

@taiseiue
Last active April 27, 2026 08:01
Show Gist options
  • Select an option

  • Save taiseiue/1c9915e74984118b94631a12e4cf2404 to your computer and use it in GitHub Desktop.

Select an option

Save taiseiue/1c9915e74984118b94631a12e4cf2404 to your computer and use it in GitHub Desktop.
OSC52経由でのpbcopy,pbpaste
#!/usr/bin/env bash
# pbcopy
# Usage: echo "text" | pbcopy
# pbcopy < file.txt
# pbcopy "text"
set -euo pipefail
# 引数があればそれを使い、なければstdinを読む
if [[ $# -gt 0 ]]; then
data="$*"
else
data="$(cat)"
fi
# macOSとGNUのもので挙動が違うので出し分け
if base64 --version &>/dev/null 2>&1; then
encoded="$(printf '%s' "$data" | base64 | tr -d '\n')"
else
encoded="$(printf '%s' "$data" | base64 -w 0)"
fi
# tmux/screen内ではラッピングする必要がある
emit_osc52() {
local payload="$1"
printf '\033]52;c;%s\007' "$payload"
}
if [[ -n "${TMUX:-}" ]]; then
# tmuxならDCSで囲む
printf '\033Ptmux;\033\033]52;c;%s\007\033\\' "$encoded"
elif [[ "${TERM:-}" == screen* ]]; then
# screen
printf '\033P\033]52;c;%s\007\033\\' "$encoded"
else
emit_osc52 "$encoded"
fi
# /dev/ttyが使えるならそこに出力
if [[ -w /dev/tty ]]; then
:
fi
# ttyへ直接書き込む
{
if [[ -n "${TMUX:-}" ]]; then
printf '\033Ptmux;\033\033]52;c;%s\007\033\\' "$encoded"
elif [[ "${TERM:-}" == screen* ]]; then
printf '\033P\033]52;c;%s\007\033\\' "$encoded"
else
printf '\033]52;c;%s\007' "$encoded"
fi
} > /dev/tty
byte_count=${#data}
printf '%d bytes copied to clipboard\n' "$byte_count" >&2
#!/usr/bin/env bash
# pbpaste
# Note: Requires terminal emulators support.
# Available: iTerm2, xterm, wezterm
# Usage: pbpaste
# pbpaste | grep "pattern"
set -euo pipefail
# OSC52 paste request: \e]52;c;?\a を送るとターミナルが
# \e]52;c;<base64>\a で応答する
request_clipboard() {
local tty_dev="/dev/tty"
if [[ ! -r "$tty_dev" ]] || [[ ! -w "$tty_dev" ]]; then
echo "pbpaste: /dev/tty にアクセスできません" >&2
exit 1
fi
local old_stty
old_stty=$(stty -g < "$tty_dev")
trap 'stty "$old_stty" < "$tty_dev"' EXIT INT TERM
stty raw -echo min 0 time 3 < "$tty_dev"
# OSC52 paste queryを送信する
if [[ -n "${TMUX:-}" ]]; then
printf '\033Ptmux;\033\033]52;c;?\007\033\\' > "$tty_dev"
elif [[ "${TERM:-}" == screen* ]]; then
printf '\033P\033]52;c;?\007\033\\' > "$tty_dev"
else
printf '\033]52;c;?\007' > "$tty_dev"
fi
local response=""
local char
local timeout=30 # 3秒 (time 3 = 0.3秒 × 10)
local count=0
while [[ $count -lt $timeout ]]; do
char=$(dd if="$tty_dev" bs=1 count=1 2>/dev/null || true)
if [[ -z "$char" ]]; then
((count++)) || true
continue
fi
response+="$char"
count=0
# BEL (\a = \007) で終端
if [[ "$response" == *$'\007' ]]; then
break
fi
done
stty "$old_stty" < "$tty_dev"
trap - EXIT INT TERM
# レスポンス: ESC ] 52 ; c ; DATA BEL
local base64_data
base64_data=$(printf '%s' "$response" | sed 's/.*]52;c;//; s/\x07//')
if [[ -z "$base64_data" ]] || [[ "$base64_data" == "?" ]]; then
echo "pbpaste: ターミナルがOSC52 pasteに応答しませんでした" >&2
echo "ターミナルエミュレータがOSC52読み取りをサポートしているか確認してください" >&2
exit 1
fi
if base64 --version &>/dev/null 2>&1; then
printf '%s' "$base64_data" | base64 --decode
else
printf '%s' "$base64_data" | base64 -d
fi
}
request_clipboard
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment