2 # Copyright (C) 2014-2016 Ian Kelling
3 # This program is under GPL v. 3 or later, see <http://www.gnu.org/licenses/>
6 local target dest_file dest_dir
10 if [[ -L $dest_file ]]; then
11 if [[ $
(readlink
$dest_file) == "$target" ]]; then
12 # Leave the link in place, but make sure it's
13 # owner & group is as if we created it.
14 # links all get 777 perms, so
15 # we already know that is right.
18 if [[ $
(stat
-L -c%a
"$dest_dir") == 2???
]]; then
19 grp
=$
(stat
-L -c%g
"$dest_dir") ||
return $?
21 grp
=$
(id
-g) ||
return $?
23 if [[ $EUID == 0 && $
(stat
-c%u
"$dest_file") != 0 ]]; then
24 chown
-h 0:$grp "$dest_file" ||
return $?
25 elif [[ $
(stat
-c%g
"$dest_file") != "$grp" ]]; then
26 chgrp
-h $grp "$dest_file" ||
return $?
31 to_remove
+=("$dest_file")
32 elif [[ -e $dest_file ]]; then
33 to_remove
+=("$dest_file")
39 lnf [OPTIONS] -T TARGET LINK_NAME (1st form)
40 lnf [OPTIONS] TARGET (2nd form)
41 lnf [OPTIONS] TARGET... DIRECTORY (3rd form)
42 Create symlinks forcefully
44 If the link already exists, make it's ownership be the same as if it was
45 newly created (only chown if we are root). Removes existing files using
46 rm -rf. Create directory of link if needed. Slightly more restrictive
49 In the 1st form, create a link to TARGET with the name LINK_NAME. In the 2nd
50 form, create a link to TARGET in the current directory. In the 3rd form, create
51 links to each TARGET in DIRECTORY.
53 -n|--dry-run Do verbose dry run.
54 -v|--verbose Print commands which modify the filesystem.
55 -h|--help Print help and exit.
63 temp
=$
(getopt
-l help,dry-run
,verbose hnTv
"$@") || usage
1
67 -n|
--dry-run) dry_run
=true
; verbose
=true
; shift ;;
68 -T) nodir
=-T; shift ;;
69 -v|
--verbose) verbose
=true
; shift ;;
70 -h|
--help) echo "$help"; return 0 ;;
72 *) echo "$0: Internal error! unexpected args: $*" ; exit 1 ;;
76 if (( $# == 0 )); then
82 if (( $# != 2 )); then
83 echo "lnf: error: expected 2 arguments with -T flag. Got $#"
88 local reset_extglob
=false
89 ! shopt extglob
>/dev
/null
&& reset_extglob
=true
93 local -a to_remove to_link
94 local ret prefix dest_dir grp target
99 dest_dir
="$(dirname "$dest_file")"
100 _lnf_existing_link
"$1" "$dest_file" "$dest_dir" ||
return $?
101 if $do_exit; then return 0; fi
102 if [[ ! -d $dest_dir ]]; then
104 if [[ -e $dest_dir ||
-L $dest_dir ]]; then
105 to_remove
+=("$dest_dir")
108 to_link
+=("$dest_file")
109 elif (( $# >= 2 )); then
111 if [[ -d $dest_dir ]]; then
112 prefix
="$dest_dir" # last arg
113 for target
in "${@:1:$(( $# - 1 ))}"; do # all but last arg
114 # Remove 1 or more trailing slashes, using.
115 dest_file
="${target%%+(/)}"
116 # remove any leading directory components, add prefix
117 dest_file
="$prefix/${target##*/}"
118 _lnf_existing_link
"$target" "$dest_file" "$dest_dir"
121 to_link
+=("${@:1:$(( $# - 1 ))}")
124 if (( ${#to_link[@]} == 0 )); then
127 to_link
+=("$dest_dir")
128 elif [[ $# -eq 1 ]]; then
130 _lnf_existing_link
"$1" "$dest_file" . ||
return $?
131 if $do_exit; then return 0; fi
133 if (( ${#to_remove[@]} >= 1 )); then
135 echo "lnf: rm -rf -- ${to_remove[*]}"
138 rm -rf -- "${to_remove[@]}"
142 $reset_extglob && shopt -u extglob
146 echo "lnf: mkdir -p $dest_dir"
149 if ! $dry_run && ! mkdir
-p "$dest_dir"; then
150 echo "lnf error: failed to make directory $dest_dir"
156 echo "lnf: ln -s $nodir -- ${to_link[*]}"
159 ln -s $nodir -- "${to_link[@]}"