Compare commits

..
22 Commits
Author SHA1 Message Date
Honney OC d2b11f7e5c fix: dest folder failsave 2026-09-18 18:38:00 +00:00
Honney OC 5cfbb620fb fix: rscp can now handle *wildcards and remote to remote 2026-09-18 18:33:53 +00:00
Hannes 60bf635a0c feat: add rscp function for rsyncing files to NAS datasets 2026-09-18 19:50:18 +02:00
Hannes 8b8be02912 some fixes 2026-08-31 21:36:32 +02:00
Hannes 6c746797e9 feat: add mkdir alias to configs/alias_useful.fish that cds into the created folder 2026-08-27 00:15:37 +02:00
Hannes 8edee10703 fix: refactor pacman alias to have default --needed in -S subcommand 2026-08-27 00:13:24 +02:00
Hannes ae6b0b227b fixing usage in other scripts 2026-08-25 23:58:46 +02:00
Your Name 444ca8c57d fix: robust AUR update detection in updateall
- replace fragile quoted test -z "$aur_qu" with count-based empty check for fish list handling
- separate success (code 0 + empty) vs error (code !=0 + empty) to avoid false up-to-date on network failures
- use string match -qr with explicit patterns for helpers messages instead of quoted glob checks
- fix global leak: use set -l answer and read -l, validate with string match -qr ^[Yy]$
- deduplicate and clarify error vs pending branches while keeping mandatory PKGBUILD review guard
2026-08-25 14:29:10 +00:00
Your Name e12d3d42cb fix: robust flatpak timeout handling in updateall
- wrap flatpak remote-ls check in timeout 5s with mktemp caching and proper exit code handling (124 = timeout)
- fix inverted -n/-z logic and uninitialized flatpak_has_updates causing Missing argument error
- distinguish timeout vs generic check failure with clear messages
- add timeout 300s guard to flatpak update and handle 124 as update timed out
- gracefully degrade when timeout binary missing instead of skipping flatpak entirely
- quote temp file paths and handle mktemp failure reliably
2026-08-25 14:27:20 +00:00
Hannes deb879b6c4 fix: Improve flatpak update check logic 2026-08-24 14:07:09 +02:00
Hannes 7a1e432231 fix: update flatpak update check to handle timeouts and null output 2026-08-24 13:55:06 +02:00
Hannes f1fb649e98 refactor: Moved updateall and added some lines to clearyfy in arch_config.fish 2026-08-23 20:24:26 +02:00
Hannes 1f17b19f1e refactor: Improve umount and force_unmount functions for better error handling and output formatting 2026-08-22 23:57:13 +02:00
Hannes 3305f5a543 feat: Update updateall script
Updated updateall to include comprehensive test coverage, support for Vencord, and improved output formatting.
2026-08-22 23:13:18 +02:00
Hannes f88dbcc6f4 added hypridle-reload 2026-08-22 22:01:15 +02:00
Hannes 65c3034b91 added fish greeting 2026-08-22 16:21:10 +02:00
Hannes 485d8768f9 updateall also runs matugen updates 2026-08-22 15:42:06 +02:00
Hannes 275121a638 added -r as standard to rm 2026-08-21 22:28:17 +02:00
Hannes 65ebd5e171 added a function to easily generate ssh key 2026-08-21 21:14:43 +02:00
Hannes 9ed74e1028 added igrep for inverse grep 2026-08-21 19:47:34 +02:00
Hannes a3f0b42353 Merge remote-tracking branch 'refs/remotes/origin/main' 2026-08-20 17:46:01 +02:00
Hannes 318420eba6 fixes to mount scripts 2026-08-20 17:45:44 +02:00
8 changed files with 980 additions and 168 deletions
-4
View File
@@ -52,8 +52,4 @@ if status is-interactive
source $HOME/.config/fish/configs/default_config.fish
end
end
if test -e /usr/bin/fastfetch
fastfetch
end
end
+13 -1
View File
@@ -5,8 +5,14 @@
alias mount_all='sudo $HOME/.config/fish/scripts/mount.sh'
alias umount_all='sudo $HOME/.config/fish/scripts/umount.sh'
function mkdir
command mkdir $argv
and cd -- $argv[-1]
end
################ COMMON ALIASES ################################################
alias rm='rm -r'
alias remount='sudo mount -a'
alias mount_config='sudo nano /etc/fstab'
alias own='sudo chown -R "$USER":"$USER"'
@@ -287,7 +293,7 @@ end
################ GREP #########################################################
if test -e /usr/bin/ugrep
if command -q ugrep
alias grep='ugrep --color=auto'
alias egrep='ugrep -E --color=auto'
alias fgrep='ugrep -F --color=auto'
@@ -296,6 +302,12 @@ else
alias fgrep='grep -F'
end
if command -q ugrep
alias igrep='ugrep --color=auto --invert-match'
else if command -q grep
alias igrep='grep --color=auto --invert-match'
end
################ WGET #########################################################
if test -e /usr/bin/wget
+574 -116
View File
@@ -4,123 +4,20 @@
source $HOME/.config/fish/configs/uni.fish
function get_pkg_version
set pkg $argv[1]
if pacman -Q $pkg >/dev/null 2>&1
pacman -Q $pkg
else
echo ""
end
end
function kernel_to_pkg
set kernel (uname -r)
if string match -q "*zen*" $kernel
echo linux-zen
else if string match -q "*lts*" $kernel
echo linux-lts
else if string match -q "*hardened*" $kernel
echo linux-hardened
else
echo linux
end
end
function updateall
######## Get current versions (if installed)
if command -q discord
set discord_version (get_pkg_version discord)
end
set kernel_version (get_pkg_version (kernel_to_pkg))
######## Run PACMAN updates
set_color cyan
echo "#### Updating pacman ####"
set_color normal
sudo pacman -Syu
######## Do additional Discord Updates
if command -q discord; and test "$discord_version" != "$(get_pkg_version discord)"
set_color cyan
echo "Discord version changed: $discord_version -> $(get_pkg_version discord)"
set_color normal
if test -e /usr/bin/curl
echo "Running Vencord installer..."
sh -c "$(curl -sS https://raw.githubusercontent.com/Vendicated/VencordInstaller/main/install.sh)"
else
echo "'curl' is not installed. Install it to update Discord plugins."
end
end
######## Run FLATPAK Updates
if test -e /usr/bin/flatpak
set_color cyan
echo "#### Updating flatpak ####"
set_color normal
flatpak update
end
######## Run GIT Updates
set_color cyan
echo "#### Updating quickshell ####"
set_color normal
if test -d ~/.config/quickshell && test -d ~/.config/quickshell/.git
pushd ~/.config/quickshell/
git pull
popd
end
set_color cyan
echo "#### Updating fish ####"
set_color normal
if test -d ~/.config/fish && test -d ~/.config/fish/.git
pushd ~/.config/fish/
git pull
popd
end
set_color cyan
echo "#### Updating hyprland ####"
set_color normal
if test -d ~/.config/hypr && test -d ~/.config/hypr/.git
pushd ~/.config/hypr/
git pull
popd
end
######## Run AUR Updates
set_color cyan
echo "#### Updating AUR ####"
set_color normal
if test -e /usr/bin/paru
paru -Sua --sudoloop
else if test -e /usr/bin/yay
yay -Sua --sudoloop
end
######## Restart if Kernel Updated
if test "$kernel_version" != "$(get_pkg_version $(kernel_to_pkg))"
set_color cyan
echo "Kernel changed: $kernel_version -> $(get_pkg_version $(kernel_to_pkg))"
read -P "Reboot now? [y/N]: " answer
set_color normal
if test "$answer" = "y" -o "$answer" = "Y"
reboot
else
echo "Skipping reboot."
end
end
end
# ------------------------------------------------------------------------------
# Aliases & small utilities (kept after main for readability)
# ------------------------------------------------------------------------------
alias fixpacman='sudo rm -f /var/lib/pacman/db.lck'
function pacman
if test "$argv[1]" = "-S"
command pacman -S --needed $argv[2..]
else
command pacman $argv
end
end
if test -e /usr/bin/expac
alias rip='expac --timefmt="%Y-%m-%d %T" "%l\t%n %v" | sort | tail -200 | nl'
alias big='expac -H M "%m\t%n" | sort -h | nl'
@@ -146,17 +43,28 @@ end
if test -e /usr/bin/tmux
function quickshell-reload
if test -e /usr/bin/quickshell
if command -q quickshell
tmux kill-session -t qs 2>/dev/null
tmux new-session -d -s qs 'quickshell'
tmux new-session -d -s qs 'env QT_QPA_PLATFORMTHEME=qt6ct quickshell'
else
echo "'quickshell' is not installed. Install it via your package manager."
end
end
function hypridle-reload
if command -q hypridle
tmux kill-session -t hypridle 2>/dev/null
tmux new-session -d -s hypridle 'hypridle'
else
echo "'hypridle' is not installed. Install it via your package manager."
end
end
else
function quickshell-reload
echo "'tmux' is not installed. Install it via your package manager."
end
function hypridle-reload
echo "'tmux' is not installed. Install it via your package manager."
end
end
if test -e /usr/bin/reflector
@@ -174,3 +82,553 @@ function pacman-autoremove
echo "No orphaned packages."
end
end
# ------------------------------------------------------------------------------
# Helpers
# ------------------------------------------------------------------------------
function get_pkg_version
set pkg $argv[1]
if pacman -Q $pkg >/dev/null 2>&1
pacman -Q $pkg
else
echo ""
end
end
function kernel_to_pkg
set kernel (uname -r)
if string match -q "*zen*" $kernel
echo linux-zen
else if string match -q "*lts*" $kernel
echo linux-lts
else if string match -q "*hardened*" $kernel
echo linux-hardened
else
echo linux
end
end
# Print a colored section header (cyan)
function _print_section
set -l title $argv[1]
set_color cyan
echo "#### $title ####"
set_color normal
end
# ------------------------------------------------------------------------------
# Main: updateall
# Orchestrates system + dotfiles updates with:
# - suppression when up-to-date (short "up to date" line)
# - full output when updates exist (do NOT suppress)
# - per-component summary at the end (git ones are important)
# - --vencord / -v flag gates all discord/vencord queries
# - AUR diff is mandatory (cannot be skipped)
#
# Order (readability):
# 1) Flags / summary init / version snapshot
# 2) System packages: pacman -> AUR -> flatpak
# 3) Dotfiles: git (quickshell/fish/hypr/matugen)
# 4) Optional: discord/vencord (only if --vencord)
# 5) Kernel reboot prompt
# 6) Summary
# ------------------------------------------------------------------------------
function updateall
# ---- 1) Flags ----
set -l _do_vencord 0
argparse --name=updateall 'v/vencord' 'h/help' -- $argv
or return 1
if set -q _flag_help
echo "Usage: updateall [--vencord] [-h/--help]"
echo " --vencord, -v also (re)install/update Vencord when Discord changes or force when up to date"
echo " -h, --help show this help"
return 0
end
if set -q _flag_vencord
set _do_vencord 1
end
if test (count $argv) -gt 0
echo "updateall: unknown argument: $argv" >&2
return 1
end
# Summary tracking - parallel arrays: labels / status / color
set -l _labels
set -l _status
set -l _colors
# Version snapshot (discord only if --vencord, per request)
set -l discord_version ""
if test $_do_vencord -eq 1; and type -q discord
set discord_version (get_pkg_version discord)
end
set -l kernel_version (get_pkg_version (kernel_to_pkg))
# ----------------------------------------------------------------------
# 2) System packages
# ----------------------------------------------------------------------
# ---- pacman (core) ----
_print_section "Updating pacman"
set -l pacman_status "skipped"
set -l pacman_color yellow
if type -q pacman
set -l has_updates 1
set -l qu_output ""
if type -q checkupdates
set qu_output (checkupdates 2>&1)
if test $status -eq 0 -a -n "$qu_output"
set has_updates 0
else if string match -q "*ERROR*Failed to synchronize*" "$qu_output"
set has_updates 0
end
else
set qu_output (pacman -Qu 2>&1)
if test $status -eq 0
set has_updates 0
else if string match -q "*database*" "$qu_output"
set has_updates 0
end
end
if test $has_updates -eq 1
echo "pacman: up to date"
set pacman_status "up to date"
set pacman_color green
else
if test -n "$qu_output"
echo "Pending pacman updates:"
printf "%s\n" $qu_output
end
sudo pacman -Syu
set -l pacman_code $status
if test $pacman_code -eq 0
set pacman_status "updated"
set pacman_color green
else
set pacman_status "failed ($pacman_code)"
set pacman_color red
end
end
else
echo "pacman not found, skipping"
set pacman_status "not installed"
set pacman_color yellow
end
set -a _labels "pacman"
set -a _status $pacman_status
set -a _colors $pacman_color
# ---- AUR (paru/yay) - directly after pacman, mandatory diff ----
_print_section "Updating AUR"
set -l aur_status "skipped"
set -l aur_color yellow
set -l aur_helper ""
if type -q paru
set aur_helper paru
else if type -q yay
set aur_helper yay
end
if test -n "$aur_helper"
set -l aur_qu ($aur_helper -Qua 2>&1)
set -l aur_qu_code $status
# Robust state detection: use (count) not quoted -z, and explicit patterns.
# aur_qu is a list (one element per line) -> count is reliable.
set -l aur_is_up_to_date 0
set -l aur_has_check_error 0
if test $aur_qu_code -eq 0
if test (count $aur_qu) -eq 0
set aur_is_up_to_date 1
else if string match -qr -- "there is nothing to do|No AUR|No packages to upgrade" $aur_qu
set aur_is_up_to_date 1
end
else
# Non-zero: only treat as up-to-date if helper explicitly says so
if string match -qr -- "there is nothing to do|No AUR" $aur_qu
set aur_is_up_to_date 1
else
set aur_has_check_error 1
end
end
if test $aur_is_up_to_date -eq 1
echo "AUR: up to date"
set aur_status "up to date"
set aur_color green
else if test $aur_has_check_error -eq 1
# Real check failure: do NOT silently report up-to-date
echo "Failed to check for AUR updates (code $aur_qu_code):"
if test (count $aur_qu) -gt 0
printf "%s\n" $aur_qu
else
echo "(no output)"
end
echo ""
set_color yellow
echo "==> AUR PKGBUILD review is MANDATORY - diff cannot be skipped."
echo " Note: PKGBUILDs must NEVER contain 'sudo'. Please verify diffs contain no 'sudo'."
set_color normal
set -l answer ""
read -l -P "Update now? [y/N]: " answer
if string match -qr -- "^[Yy]\$" "$answer"
if test "$aur_helper" = "paru"
paru -Sua --sudoloop --review
else
yay -Sua --sudoloop --diffmenu --answerdiff All --answerclean All
end
set -l _aur_code $status
if test $_aur_code -eq 0
set aur_status "updated"
set aur_color green
else
set aur_status "failed ($_aur_code)"
set aur_color red
end
end
else
echo "Pending AUR updates:"
printf "%s\n" $aur_qu
set_color yellow
echo "==> AUR PKGBUILD review is MANDATORY - diff cannot be skipped."
echo " Note: PKGBUILDs must NEVER contain 'sudo'. Please verify diffs contain no 'sudo'."
set_color normal
set -l answer ""
read -l -P "Update now? [y/N]: " answer
if string match -qr -- "^[Yy]\$" "$answer"
if test "$aur_helper" = "paru"
paru -Sua --sudoloop --review
else
yay -Sua --sudoloop --diffmenu --answerdiff All --answerclean All
end
set -l _aur_code $status
if test $_aur_code -eq 0
set aur_status "updated"
set aur_color green
else
set aur_status "failed ($_aur_code)"
set aur_color red
end
end
end
else
echo "No AUR helper (paru/yay) found, skipping"
set aur_status "no helper"
set aur_color yellow
end
set -a _labels "AUR"
set -a _status $aur_status
set -a _colors $aur_color
# ---- flatpak ----
_print_section "Updating flatpak"
set -l flatpak_status "skipped"
set -l flatpak_color yellow
if not type -q flatpak
set flatpak_status "not installed"
set flatpak_color yellow
else
set -l flatpak_tmp (mktemp)
if test $status -ne 0 -o -z "$flatpak_tmp"
echo "flatpak: failed to create temp file"
set flatpak_status "update check failed"
set flatpak_color red
else
set -l flatpak_check_code
set -l flatpak_check
if type -q timeout
timeout 5s flatpak remote-ls --updates >"$flatpak_tmp" 2>&1
set flatpak_check_code $status
else
flatpak remote-ls --updates >"$flatpak_tmp" 2>&1
set flatpak_check_code $status
end
set flatpak_check (cat "$flatpak_tmp")
rm -f "$flatpak_tmp"
# flatpak_has_updates: 0 = pending updates, 1 = up to date, 2 = check failed/timeout
set -l flatpak_has_updates 0
if test $flatpak_check_code -eq 0
if test -z "$flatpak_check"
set flatpak_has_updates 1
else
set flatpak_has_updates 0
end
else if test $flatpak_check_code -eq 124
set flatpak_has_updates 2
else
set flatpak_has_updates 2
end
if test $flatpak_has_updates -eq 2
if test $flatpak_check_code -eq 124
echo "flatpak: update check timed out (5s)"
else
echo "flatpak: update check failed"
end
if test -n "$flatpak_check"
printf "%s\n" $flatpak_check
end
set flatpak_status "update check failed"
set flatpak_color red
else if test $flatpak_has_updates -eq 1
echo "flatpak: up to date"
set flatpak_status "up to date"
set flatpak_color green
else
if test -n "$flatpak_check"
echo "Pending flatpak updates:"
printf "%s\n" $flatpak_check
end
set -l flatpak_code
if type -q timeout
timeout 300s flatpak update
set flatpak_code $status
else
flatpak update
set flatpak_code $status
end
if test $flatpak_code -eq 124
echo "flatpak: update timed out (300s)"
set flatpak_status "update timed out"
set flatpak_color red
else if test $flatpak_code -eq 0
set flatpak_status "updated"
set flatpak_color green
else
set flatpak_status "failed ($flatpak_code)"
set flatpak_color red
end
end
end
end
set -a _labels "flatpak"
set -a _status $flatpak_status
set -a _colors $flatpak_color
# ----------------------------------------------------------------------
# 3) Dotfiles (git) - always show per-repo success/fail, suppress only when up to date
# ----------------------------------------------------------------------
set -l git_repos "quickshell:$HOME/.config/quickshell" "fish:$HOME/.config/fish" "hypr:$HOME/.config/hypr" "matugen:$HOME/.config/matugen"
if not type -q git
echo "git not installed"
set git_status "git not installed"
set git_color yellow
set -a _labels "git"
set -a _status $git_status
set -a _colors $git_color
else
for entry in $git_repos
set -l parts (string split ":" -- $entry)
set -l name $parts[1]
set -l path $parts[2]
_print_section "Updating $name"
set -l git_status "skipped"
set -l git_color yellow
if not test -d $path/.git
echo "skipped: $path not a git repo"
set git_status "not tracked"
set git_color yellow
else
git -C $path fetch --quiet >/dev/null 2>&1
set -l fetch_code $status
set -l behind_str (git -C $path rev-list --count HEAD..@{u} 2>&1)
set -l behind_code $status
if test $fetch_code -ne 0 -o $behind_code -ne 0
set -l git_output (git -C $path pull 2>&1)
set -l git_code $status
if test $git_code -ne 0
printf "%s\n" $git_output
set git_status "failed ($git_code)"
set git_color red
else if string match -q "*Already up to date*" "$git_output"
echo "$name: up to date"
set git_status "up to date"
set git_color green
else
printf "%s\n" $git_output
set git_status "updated"
set git_color green
end
else if test "$behind_str" = "0"
echo "$name: up to date"
set git_status "up to date"
set git_color green
else
git -C $path pull
set -l pull_code $status
if test $pull_code -eq 0
set git_status "updated"
set git_color green
else
set git_status "failed ($pull_code)"
set git_color red
end
end
end
set -a _labels "git:$name"
set -a _status $git_status
set -a _colors $git_color
end
end
# ----------------------------------------------------------------------
# 4) Optional: Discord / Vencord (only when --vencord)
# ----------------------------------------------------------------------
set -l discord_status "skipped"
set -l discord_color yellow
set -l vencord_status "skipped"
set -l vencord_color yellow
set -l _need_vencord_label 0
if test $_do_vencord -eq 0
set discord_status "skipped (use --vencord)"
set discord_color yellow
set vencord_status "skipped (use --vencord)"
set vencord_color yellow
else if type -q discord
set -l new_discord_version (get_pkg_version discord)
if test "$discord_version" != "$new_discord_version"
set_color cyan
echo "Discord version changed: $discord_version -> $new_discord_version"
set_color normal
set discord_status "updated ($new_discord_version)"
set discord_color green
if test $_do_vencord -eq 1
set _need_vencord_label 1
if type -q curl
echo "Running Vencord installer (--vencord)..."
sh -c "$(curl -sS https://raw.githubusercontent.com/Vendicated/VencordInstaller/main/install.sh)"
if test $status -eq 0
set vencord_status "updated ($new_discord_version)"
set vencord_color green
set discord_status "$discord_status + vencord ok"
else
set vencord_status "failed ($status)"
set vencord_color red
set discord_status "$discord_status + vencord failed"
set discord_color red
end
else
echo "'curl' is not installed. Install it to update Discord plugins."
set vencord_status "curl missing"
set vencord_color yellow
set discord_status "$discord_status + vencord skipped (curl missing)"
set discord_color yellow
end
else
set vencord_status "skipped (discord updated, use --vencord)"
set vencord_color yellow
echo "Discord updated, skipping Vencord (use --vencord to update)"
end
else
set discord_status "up to date ($new_discord_version)"
set discord_color green
if test $_do_vencord -eq 1
set _need_vencord_label 1
set_color cyan
echo "Discord up to date ($new_discord_version), but --vencord given: running Vencord installer..."
set_color normal
if type -q curl
sh -c "$(curl -sS https://raw.githubusercontent.com/Vendicated/VencordInstaller/main/install.sh)"
if test $status -eq 0
set vencord_status "refreshed/updated"
set vencord_color green
set discord_status "$discord_status + vencord ok"
else
set vencord_status "failed ($status)"
set vencord_color red
set discord_status "$discord_status + vencord failed"
end
else
echo "'curl' is not installed. Install it to update Discord plugins."
set vencord_status "curl missing"
set vencord_color yellow
end
else
set vencord_status "skipped (use --vencord)"
set vencord_color yellow
end
end
else
set discord_status "not installed"
set discord_color yellow
if test $_do_vencord -eq 1
set _need_vencord_label 1
set vencord_status "skipped (discord not installed)"
set vencord_color yellow
echo "Discord not installed, skipping Vencord"
end
end
set -a _labels "discord"
set -a _status $discord_status
set -a _colors $discord_color
if test $_do_vencord -eq 1
set -a _labels "vencord"
set -a _status $vencord_status
set -a _colors $vencord_color
end
# ----------------------------------------------------------------------
# 5) Kernel (needs reboot if package changed)
# ----------------------------------------------------------------------
set -l kernel_status "up to date"
set -l kernel_color green
set -l new_kernel_version (get_pkg_version (kernel_to_pkg))
if test "$kernel_version" != "$new_kernel_version"
set_color cyan
echo "Kernel changed: $kernel_version -> $new_kernel_version"
set_color normal
set kernel_status "updated ($kernel_version -> $new_kernel_version)"
set kernel_color green
set answer ""
read -P "Reboot now? [y/N]: " answer
if test "$answer" = "y" -o "$answer" = "Y"
reboot
else
echo "Skipping reboot."
set kernel_status "$kernel_status (reboot skipped)"
end
else
set kernel_status "up to date ($new_kernel_version)"
set kernel_color green
end
set -a _labels "kernel"
set -a _status $kernel_status
set -a _colors $kernel_color
# ----------------------------------------------------------------------
# 6) Summary (always shown, per-component success/fail)
# ----------------------------------------------------------------------
echo ""
set_color --bold cyan
echo "########################"
echo "#### Update Summary ####"
echo "########################"
set_color normal
set -l max_len 0
for l in $_labels
if test (string length -- $l) -gt $max_len
set max_len (string length -- $l)
end
end
for i in (seq (count $_labels))
set -l label $_labels[$i]
set -l stat $_status[$i]
set -l col $_colors[$i]
set -l pad (string repeat -n (math $max_len - (string length -- $label)) " ")
set_color normal
printf "%s%s : " $label $pad
set_color $col
echo $stat
set_color normal
end
end
+5
View File
@@ -0,0 +1,5 @@
function fish_greeting
if test -e /usr/bin/fastfetch
fastfetch
end
end
+313
View File
@@ -0,0 +1,313 @@
function rscp --description "Copy SOURCE... to DESTINATION via rsync (local or NAS)"
# rscp: rsync-based copy that understands the local NAS mounts.
#
# Usage: rscp [-y|--yes] [--] SOURCE... DESTINATION
#
# Only the LAST argument is the destination; everything before it is a
# source. That way wildcards just work: `rscp *.mkv /dest/` copies every
# match instead of silently dropping all but the first one.
#
# Paths under /home/honney/mount/<dataset> are transferred directly as
# NAS:/mnt/tank/<dataset>, so rsync talks to the NAS instead of going
# through the local mount. Purely local (PC -> PC) copies use rsync with
# the same flags as rcp (-avh --progress).
# NAS -> NAS copies run on the NAS itself via `ssh <host> ... rsync`,
# keeping the flags of the "to remote" direction (sudo + chown).
set -l force 0
# Only leading flags are options; everything else (even a source starting
# with "-") is a path. Use `--` to end option parsing explicitly.
while test (count $argv) -gt 0
switch $argv[1]
case -y --yes
set force 1
set -e argv[1]
case -h --help
echo "Usage: rscp [-y|--yes] [--] SOURCE... DESTINATION"
echo ""
echo "Copy one or more SOURCEs to DESTINATION with rsync."
echo "Only the last argument is the destination; everything before it is a source,"
echo "so wildcards at the source (e.g. rscp *.mkv /dest/) work as expected."
echo ""
echo "Options:"
echo " -y, --yes skip the overwrite confirmation prompt"
echo " -h, --help show this help"
return 0
case --
set -e argv[1]
break
case '-*'
echo "rscp: unknown option: $argv[1]" >&2
echo "Usage: rscp [-y|--yes] [--] SOURCE... DESTINATION" >&2
return 1
case '*'
break
end
end
if test (count $argv) -lt 2
echo "Usage: rscp [-y|--yes] [--] SOURCE... DESTINATION" >&2
return 1
end
if not command -q rsync
echo "rscp: 'rsync' is not installed. Install it via your package manager." >&2
return 1
end
# Mount <-> NAS dataset mapping (keep both lists in the same order)
set -l mounts /home/honney/mount/Media /home/honney/mount/Data /home/honney/mount/Ripping /home/honney/mount/Apps
set -l remotes NAS:/mnt/tank/Media NAS:/mnt/tank/Data NAS:/mnt/tank/Ripping NAS:/mnt/tank/Apps
# Only the last argument is the destination; everything before it is a
# source, so any number of (wildcard-expanded) sources is supported.
set -l n (count $argv)
set -l last_src (math $n - 1)
set -l dst_raw $argv[$n]
set -l srcs_raw $argv[1..$last_src]
# Translate sources: local mount path -> NAS:<path>. For each source keep
# the translated path, a 1/0 remote flag, the remote host and the path
# as seen on the remote side (without the "HOST:" prefix).
set -l srcs
set -l src_remote_flags
set -l src_hosts
set -l src_naslocals
set -l hosts
for raw in $srcs_raw
set -l s $raw
set -l is_remote 0
for i in (seq (count $mounts))
if string match -q -- $mounts[$i] $s; or string match -q -- "$mounts[$i]/*" $s
set s (string replace -f -- $mounts[$i] $remotes[$i] $s)
set is_remote 1
break
end
end
# Also accept an explicit HOST:/path given directly.
if test $is_remote -eq 0
if string match -qr '^[A-Za-z0-9_.@-]+:/' -- $s
set is_remote 1
end
end
set -l host ""
set -l naslocal $s
if test $is_remote -eq 1
set -l parts (string split -m1 : -- $s)
set host $parts[1]
set naslocal (string join : -- $parts[2..-1])
if not contains -- $host $hosts
set -a hosts $host
end
end
set -a srcs $s
set -a src_remote_flags $is_remote
set -a src_hosts $host
set -a src_naslocals $naslocal
end
# Translate the destination the same way (it is always $argv[-1]).
set -l dst $dst_raw
set -l dst_remote 0
for i in (seq (count $mounts))
if string match -q -- $mounts[$i] $dst; or string match -q -- "$mounts[$i]/*" $dst
set dst (string replace -f -- $mounts[$i] $remotes[$i] $dst)
set dst_remote 1
break
end
end
if test $dst_remote -eq 0
if string match -qr '^[A-Za-z0-9_.@-]+:/' -- $dst
set dst_remote 1
end
end
set -l dst_host ""
set -l dst_naslocal $dst
if test $dst_remote -eq 1
set -l dparts (string split -m1 : -- $dst)
set dst_host $dparts[1]
set dst_naslocal (string join : -- $dparts[2..-1])
if not contains -- $dst_host $hosts
set -a hosts $dst_host
end
end
set -l n_src_remote 0
for f in $src_remote_flags
if test $f -eq 1
set n_src_remote (math $n_src_remote + 1)
end
end
set -l any_remote 0
if test $dst_remote -eq 1; or test $n_src_remote -gt 0
set any_remote 1
end
# Failsafe: when a remote side is involved, verify ssh actually knows the
# host (e.g. via ~/.ssh/config) before copying anything. The probe is
# non-interactive; the real transfer below still uses your normal ssh
# config/auth (password prompt included).
if test $any_remote -eq 1
if not command -q ssh
echo "rscp: 'ssh' is not installed, but a remote copy was requested." >&2
return 1
end
if test (count $hosts) -gt 1
echo "rscp: copies between different remote hosts are not supported: $hosts" >&2
return 1
end
for h in $hosts
set -l probe_err (ssh -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null $h true 2>&1)
set -l probe_st $status
if test $probe_st -ne 0
if string match -qi -- "*could not resolve*" $probe_err; or string match -qi -- "*name or service not known*" $probe_err; or string match -qi -- "*no address associated*" $probe_err; or string match -qi -- "*nodename nor servname*" $probe_err
echo "rscp: ssh host '$h' is unknown (check the 'Host $h' entry in ~/.ssh/config)." >&2
return 1
else if string match -qi -- "*permission denied*" $probe_err; or string match -qi -- "*authentication fail*" $probe_err
echo "rscp: note: key auth for '$h' failed; continuing, ssh may ask for a password." >&2
else
echo "rscp: cannot reach ssh host '$h': $probe_err" >&2
return 1
end
end
end
end
# Failsafe: if the destination already exists *and has content*, ask
# before overwriting. An existing but empty directory is not worth
# asking about - there is nothing that could be lost.
set -l dst_disp $dst_raw
if test "$dst" != "$dst_raw"
set dst_disp "$dst_raw ($dst)"
end
set -l dst_exists 0
set -l dst_is_dir 0
set -l dst_nonempty 0
if test $dst_remote -eq 1
set -l qdst (__rscp_shquote $dst_naslocal)
if ssh $dst_host test -e $qdst
set dst_exists 1
else if ssh $dst_host test -L $qdst
set dst_exists 1
end
if test $dst_exists -eq 1
if ssh $dst_host test -d $qdst
set dst_is_dir 1
set -l listing (ssh $dst_host ls -A $qdst 2>/dev/null)
set -l ls_st $status
if test $ls_st -ne 0; or test (count $listing) -gt 0
# Unlistable -> stay safe and treat it as non-empty.
set dst_nonempty 1
end
else
# File (or symlink): overwriting would replace content.
set dst_nonempty 1
end
end
else
if test -e "$dst"; or test -L "$dst"
set dst_exists 1
end
if test $dst_exists -eq 1
if test -d "$dst"
set dst_is_dir 1
set -l listing (command ls -A -- "$dst" 2>/dev/null)
set -l ls_st $status
if test $ls_st -ne 0; or test (count $listing) -gt 0
set dst_nonempty 1
end
else
set dst_nonempty 1
end
end
end
if test $dst_nonempty -eq 1
if test (count $srcs) -gt 1; and test $dst_is_dir -eq 0
echo "rscp: destination '$dst_disp' exists and is not a directory, but multiple sources were given." >&2
return 1
end
if test $force -eq 0
if not isatty stdin
echo "rscp: destination '$dst_disp' already exists and is not empty. Re-run interactively or pass -y/--yes to overwrite." >&2
return 1
end
if not read -l -P "rscp: destination '$dst_disp' already exists and is not empty. Overwrite/merge? [y/N] " confirm_raw
echo "rscp: aborted." >&2
return 1
end
switch (string lower -- (string trim -- "$confirm_raw"))
case y yes
# proceed
case '*'
echo "rscp: aborted."
return 1
end
end
end
# --no-perms: required - TrueNAS ZFS datasets use SMB ACLs and reject unix perms preservation
set -l base_opts -avh --no-perms --info=progress2 --partial
# Flags of the "to remote" direction; remote-to-remote via ssh on the NAS
# keeps these same flags (sudo for the chown, which needs root).
set -l to_remote_opts --rsync-path="sudo -n rsync" --chown=1000:0
if test $any_remote -eq 0
# PC -> PC goes through rsync with the same flags as rcp (no NAS-only
# --no-perms/--chown permission workarounds needed locally).
set -l local_opts -avh --progress
echo rsync $local_opts -- $srcs $dst
command rsync $local_opts -- $srcs $dst
return $status
end
if test $n_src_remote -eq (count $srcs); and test $dst_remote -eq 1
# NAS -> NAS: run rsync directly on the NAS itself. Only one remote
# host is possible here (checked above), so $hosts[1] is that host.
set -l rsrcs
for nl in $src_naslocals
set -a rsrcs (__rscp_shquote $nl)
end
set -l rdst (__rscp_shquote $dst_naslocal)
set -l ssh_tty
if isatty stdin
set ssh_tty -t
end
echo ssh $ssh_tty $hosts[1] sudo -n rsync $base_opts --chown=1000:0 -- $rsrcs $rdst
command ssh $ssh_tty $hosts[1] sudo -n rsync $base_opts --chown=1000:0 -- $rsrcs $rdst
return $status
end
if test $n_src_remote -gt 0; and test $n_src_remote -lt (count $srcs)
# Mixed local and remote sources cannot go into a single rsync call,
# so copy source by source. The overwrite question above was already
# asked once, hence -y here (each call still re-checks everything else).
for k in (seq (count $srcs))
rscp -y -- $srcs_raw[$k] $dst_raw
set -l st $status
if test $st -ne 0
return $st
end
end
return 0
end
# Single-sided copy: local -> NAS, NAS -> local, or one source of any kind.
set -l call_opts $base_opts
if test $dst_remote -eq 1
set -a call_opts $to_remote_opts
else if test $n_src_remote -gt 0
set -a call_opts --rsync-path="sudo -n rsync"
end
echo rsync $call_opts -- $srcs $dst
command rsync $call_opts -- $srcs $dst
return $status
end
function __rscp_shquote --description "Single-quote strings for a POSIX remote shell"
# Usage: __rscp_shquote STRING... ; prints each string single-quoted.
for s in $argv
set -l e (string replace -a -- "'" "'\"'\"'" $s)
echo "'$e'"
end
end
+16
View File
@@ -0,0 +1,16 @@
function sshkey --description "generate a SSH key"
if test (count $argv) -ne 1
echo "Usage: sshkey <file>"
echo "example: sshkey ~/.ssh/your_key_name"
return 1
end
set file $argv[1]
if test -e "$file"
echo "Error: $file already exists."
return 1
end
ssh-keygen -t ed25519 -f "$file" -C "$(hostname)" -N ""
end
+43 -37
View File
@@ -20,52 +20,58 @@ mount_cifs() {
echo "Successfully mounted: $mount_point"
}
function is_mounted() {
mountpoint -q "$1"
}
# Mount FRITZ.NAS (from fstab line 1)
if ping -c 1 -W 1 "$HOST" >/dev/null 2>&1; then
if ! is_mounted "/home/honney/mount/fritzNAS"; then
if ping -c 1 -W 1 "192.168.1.1" >/dev/null 2>&1; then
mount_cifs "//192.168.1.1/FRITZ.NAS" \
"/home/honney/mount/fritzNAS" \
"credentials=/home/honney/.cred/samba_credentials,uid=1000,gid=1000,vers=3.0,cache=none,nounix,noserverino,file_mode=0755,dir_mode=0755"
fi
fi
echo ""
mount_points=("/home/honney/mount/Data" "/home/honney/mount/Media" "/home/honney/mount/Ripping" "/home/honney/mount/Apps")
if ping -c 1 -W 1 "192.168.188.122" >/dev/null 2>&1; then
# Mount Data (from fstab line 2)
mount_cifs "//100.64.0.3/Data" \
"/home/honney/mount/Data" \
"credentials=/home/honney/.cred/trueNAS_credentials,vers=3.1.1,iocharset=utf8,rw,uid=1000,gid=1000,nounix,noperm,file_mode=0660,dir_mode=0770,hard,intr,cache=loose"
# Mount Media (from fstab line 3)
mount_cifs "//100.64.0.3/Media" \
"/home/honney/mount/Media" \
"credentials=/home/honney/.cred/trueNAS_credentials,vers=3.1.1,iocharset=utf8,rw,uid=1000,gid=1000,nounix,noperm,file_mode=0660,dir_mode=0770,hard,intr,cache=loose"
# Mount Ripping (from fstab line 4)
mount_cifs "//100.64.0.3/Ripping" \
"/home/honney/mount/Ripping" \
"credentials=/home/honney/.cred/trueNAS_credentials,vers=3.1.1,iocharset=utf8,rw,uid=1000,gid=1000,nounix,noperm,file_mode=0660,dir_mode=0770,hard,intr,cache=loose"
# Mount Apps (from fstab line 5)
mount_cifs "//100.64.0.3/Apps" \
"/home/honney/mount/Apps" \
echo "192.168.188.122, mounting shares..."
echo ""
mounts=("//192.168.188.122/Data" "//192.168.188.122/Media" "//192.168.188.122/Ripping" "//192.168.188.122/Apps")
for i in "${!mounts[@]}"; do
mount="${mounts[$i]}"
mount_point="${mount_points[$i]}"
echo "Checking $mount_point"
if ! is_mounted "$mount_point"; then
echo "$mount_point unmounted"
mount_cifs "$mount" \
"$mount_point" \
"credentials=/home/honney/.cred/trueNAS_credentials,vers=3.1.1,iocharset=utf8,rw,uid=1000,gid=1000,nounix,noperm,file_mode=0660,dir_mode=0770,hard,intr,cache=loose"
else
echo "$mount_point already mounted"
fi
echo ""
done
elif ping -c 1 -W 1 "100.64.0.3" >/dev/null 2>&1; then
# Mount Data (from fstab line 2)
mount_cifs "//192.168.188.122/Data" \
"/home/honney/mount/Data" \
"credentials=/home/honney/.cred/trueNAS_credentials,vers=3.1.1,iocharset=utf8,rw,uid=1000,gid=1000,nounix,noperm,file_mode=0660,dir_mode=0770,hard,intr,cache=loose"
# Mount Media (from fstab line 3)
mount_cifs "//192.168.188.122/Media" \
"/home/honney/mount/Media" \
"credentials=/home/honney/.cred/trueNAS_credentials,vers=3.1.1,iocharset=utf8,rw,uid=1000,gid=1000,nounix,noperm,file_mode=0660,dir_mode=0770,hard,intr,cache=loose"
# Mount Ripping (from fstab line 4)
mount_cifs "//192.168.188.122/Ripping" \
"/home/honney/mount/Ripping" \
"credentials=/home/honney/.cred/trueNAS_credentials,vers=3.1.1,iocharset=utf8,rw,uid=1000,gid=1000,nounix,noperm,file_mode=0660,dir_mode=0770,hard,intr,cache=loose"
# Mount Apps (from fstab line 5)
mount_cifs "//192.168.188.122/Apps" \
"/home/honney/mount/Apps" \
echo "192.168.188.122 unreachable"
echo "100.64.0.3 reachable, mounting shares..."
echo ""
mounts=("//100.64.0.3/Data" "//100.64.0.3/Media" "//100.64.0.3/Ripping" "//100.64.0.3/Apps")
for i in "${!mounts[@]}"; do
mount="${mounts[$i]}"
mount_point="${mount_points[$i]}"
echo "Checking $mount_point"
if ! is_mounted "$mount_point"; then
echo "$mount_point unmounted"
mount_cifs "$mount" \
"$mount_point" \
"credentials=/home/honney/.cred/trueNAS_credentials,vers=3.1.1,iocharset=utf8,rw,uid=1000,gid=1000,nounix,noperm,file_mode=0660,dir_mode=0770,hard,intr,cache=loose"
else
echo "$mount_point already mounted"
fi
echo ""
done
fi
+10 -4
View File
@@ -7,22 +7,26 @@ function is_mounted() {
function unmount() {
if is_mounted "$1"; then
if ! sudo umount -l "$1"; then
notify-send -u critical "Unmount failed" "$1"
echo "Unmount failed: $1"
return 1
else
notify-send -u low "Unmount completed" "$1"
echo "Unmount completed: $1"
fi
else
echo "$1 is not mounted"
fi
}
function force_unmount() {
if is_mounted "$1"; then
if ! sudo umount -fl "$1"; then
notify-send -u critical "Unmount failed" "$1"
echo "Unmount failed: $1"
return 1
else
notify-send -u low "Unmount completed" "$1"
echo "Unmount completed: $1"
fi
else
echo "$1 is not mounted"
fi
}
@@ -51,3 +55,5 @@ if has_connection "192.168.1.1"; then
else
force_unmount "/home/honney/mount/fritzNAS"
fi
exit 0