clipmenu

cli clipboard manager

clipdel


#!/usr/bin/env bash

CM_REAL_DELETE=0
if [[  == -d ]]; then
    CM_REAL_DELETE=1
    shift
fi

shopt -s nullglob

cache_dir=$(clipctl cache-dir)
cache_file=$cache_dir/line_cache
lock_file=$cache_dir/lock
lock_timeout=2

if [[  == --help ]] || [[  == -h ]]; then
    cat << 'EOF'
clipdel deletes clipmenu entries matching a regex. By default, just lists what
it would delete, pass -d to do it for real. If no pattern is passed as an argument,
it will try to read one from standard input.

".*" is special, it will just nuke the entire data directory, including the
line caches and all other state.

Arguments:

    -d  Delete for real.

Environment variables:

- $CM_DIR: specify the base directory to store the cache dir in (default: $XDG_RUNTIME_DIR, $TMPDIR, or /tmp)
EOF
    exit 0
fi

if ! [[ -f $cache_file ]]; then
    printf '%s\n' "No line cache file found, no clips exist" >&2
    exit 0  # Well, this is a kind of success...
fi

if [[ -n  ]]; then
    raw_pattern=
elif ! [[ -t 0 ]]; then
    IFS= read -r raw_pattern
fi

esc_pattern=${raw_pattern//\#/'\#'}

# We use 2 separate sed commands so "esc_pattern" matches only the 'clip' text
# without the timestamp (e.g. $> clipdel '^delete_exact_match$')
sed_common_command="s#^[0-9]\+ ##;\#${esc_pattern}#"

if ! [[ $raw_pattern ]]; then
    printf '%s\n' 'No pattern provided, see --help' >&2
    exit 2
elif [[ "$raw_pattern" == ".*" ]]; then
    delete_cache_dir=1
else
    mapfile -t matches < <(
        sed -n "${sed_common_command}p" "$cache_file" |
            sort -u
    )
fi

exec {lock_fd}> "$lock_file"

if (( CM_REAL_DELETE )); then
    if (( delete_cache_dir )); then
        flock -x -w "$lock_timeout" "$lock_fd" || exit
        rm -rf -- "$cache_dir"
        mkdir -p -- "$cache_dir"
        exit 0
    else
        flock -x -w "$lock_timeout" "$lock_fd" || exit

        for match in "${matches[@]}"; do
            ck=$(cksum <<< "$match")
            rm -f -- "$cache_dir/$ck"
        done

        temp=$(mktemp)
        # sed 'h' and 'g' here means save and restore the line, so
        # timestamps are not removed from non-deleted lines. 'd' deletes the
        # line and restarts, skipping 'g'/restore.
        # https://www.gnu.org/software/sed/manual/html_node/Other-Commands.html#Other-Commands
        sed "h;${sed_common_command}d;g" "$cache_file" > "$temp"
        mv -- "$temp" "$cache_file"

        flock -u "$lock_fd"
    fi
elif (( delete_cache_dir )); then
    printf 'delete cache dir: %s\n' "$cache_dir"
elif (( ${#matches[@]} )); then
    printf '%s\n' "${matches[@]}"
fi

Download

raw zip tar