Skip to content

Instantly share code, notes, and snippets.

@AdrienHorgnies
Created November 12, 2025 09:33
Show Gist options
  • Select an option

  • Save AdrienHorgnies/2cfb104fe06e690ff432479b23d64909 to your computer and use it in GitHub Desktop.

Select an option

Save AdrienHorgnies/2cfb104fe06e690ff432479b23d64909 to your computer and use it in GitHub Desktop.
#!/bin/bash
# Copyright 2025 Adrien Horgnies
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -eu
function help {
cat <<EOF
ctrl allows to copy, cut and paste files much like ctrl-c, ctrl-x and ctrl-v key bindings.
Usage: ctrl c <file>... register the files for copying
ctrl x <file>... register the files for cutting
ctrl v paste the files in the current directory
ctrl v <path> paste the single file over the path
ctrl v dir paste the files into the given directory
ctrl l list the files registered for copying or cutting
ctrl f forget all the registered files
ctrl f <file>... forget the given files
Author: Adrien Horgnies
Authored on: 12/11/2025
Version: 1
EOF
}
if [ "$1" == 'h' ] || [ "$1" == 'help' ]
then
help
exit 0
fi
for arg in "$@"
do
if [ "$arg" == '-h' ] || [ "$arg" == '--help' ]
then
help
exit 0
fi
done
if [ -v XDG_STATE_HOME ]
then
baseDir="$XDG_STATE_HOME/ctrl"
elif [ -v HOME ]
then
baseDir="$HOME/.local/state/ctrl"
else
echo "Please define env XDG_STATE_HOME or HOME"
exit 1
fi
mkdir -p "$baseDir"/{copy,cut}
if [ "$1" == "c" ] # copy
then
shift
for f in "$@"
do
ln -s "$(realpath "$f")" "$baseDir/copy"
done
elif [ "$1" == "f" ] # forget
then
shift
if [ $# -eq 0 ]
then
rm -rf "$baseDir/"{copy,cut}
else
for f in "$@"
do
f=$(realpath "$f")
for sym in $(find "$baseDir"/{cut,copy} -type l)
do
rp=$(realpath "$sym")
if [ "$argRp" == "$rp" ]
then
rm "$sym"
fi
done
done
fi
elif [ "$1" == "l" ] # list
then
find "$baseDir/copy" -type l | xargs -r -IF realpath F | sed 's/^/ /' | sort
find "$baseDir/cut" -type l | xargs -r -IF realpath F | sed 's/^/ /' | sort
elif [ "$1" == "v" ] # paste
then
shift
if [ $# -eq 0 ]
then
target=$(pwd)
elif [ $# -ge 2 ]
then
echo "v requires 0 or 1 argument, but you have $#"
exit 1
else
target=$1
fi
count_copy="$(find "$baseDir/copy" -type l | wc -l)"
count_cut="$(find "$baseDir/cut" -type l | wc -l)"
count=$((count_copy + count_cut))
if [ -f "$target" ] && [ "$count" -ne 1 ] || ! [ -d "$target" ]
then
echo "When targeting a regular file or empty path, you can paste a single file, but you have $count"
exit 1
fi
if [ "$count_copy" -ge 1 ]
then
find "$baseDir/copy" -type l | xargs -I{} realpath '{}' | xargs -I{} cp -r '{}' "$target"
rm -rf "$baseDir/copy"
fi
if [ "$count_cut" -ge 1 ]
then
find "$baseDir/cut" -type l | xargs -I{} realpath '{}' | xargs -I{} mv '{}' "$target"
rm -rf "$baseDir/cut"
fi
elif [ "$1" == "x" ] # cut
then
shift
for f in "$@"
do
ln -s "$(realpath "$f")" "$baseDir/cut"
done
else # unknown command
help
exit 1
fi
@AdrienHorgnies
Copy link
Author

example-usage-ctrl

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment