Fish · 2006 bytes Raw Blame History
1 ## Hardlink every regular file from SRC to DST (non-recursive; includes dotfiles, skips dirs)
2 #
3 function hardlink_all --description "Hardlink every file from SRC to DST (includes dotfiles, skips dirs)"
4 set -l USAGE "Usage: hardlink_all SRC DST\n\nLinks: regular files only (top-level). Includes dotfiles. Creates DST if missing.\nNote: Hardlinks must be on the same filesystem."
5
6 if test (count $argv) -lt 1 -o "$argv[1]" = "-h" -o "$argv[1]" = "--help"
7 echo -e $USAGE
8 return 0
9 end
10
11 if test (count $argv) -ne 2
12 echo -e $USAGE >&2
13 return 1
14 end
15
16 set -l src "$argv[1]"
17 set -l dst "$argv[2]"
18
19 if not test -d "$src"
20 echo "hardlink_all: SRC is not a directory: $src" >&2
21 return 1
22 end
23
24 # Ensure destination directory exists
25 mkdir -p "$dst" ^/dev/null
26 if not test -d "$dst"
27 echo "hardlink_all: failed to create DST: $dst" >&2
28 return 1
29 end
30
31 # Best-effort filesystem check (hardlinks can't cross filesystems)
32 set -l srcfs (df -P "$src" | tail -1 | awk '{print $1}')
33 set -l dstfs (df -P "$dst" | tail -1 | awk '{print $1}')
34 if test -n "$srcfs" -a -n "$dstfs" -a "$srcfs" != "$dstfs"
35 echo "hardlink_all: SRC and DST are on different filesystems ($srcfs$dstfs); hardlinks will fail." >&2
36 return 1
37 end
38
39 set -l count 0
40 set -l fail 0
41
42 # Top-level files, including dotfiles; exclude '.' and '..'
43 set -l files $src/*
44 set -l dot1 $src/.[!.]*
45 set -l dot2 $src/..?*
46
47 for f in $files $dot1 $dot2
48 if test -f "$f"
49 ln -f "$f" "$dst/"; or begin
50 echo "hardlink_all: failed to link: $f" >&2
51 set fail 1
52 end
53 set count (math $count + 1)
54 end
55 end
56
57 if test $count -eq 0
58 echo "hardlink_all: nothing to link (no regular files found in $src)."
59 else
60 echo "hardlink_all: linked $count file(s) from $src$dst"
61 end
62
63 return $fail
64 end