fix edge case of empty string argument
[tee-unique] / appendu-function
1 #!/bin/bash
2 # Copyright (C) 2014 Ian Kelling
3 # This program is under GPL v. 3 or later, see <http://www.gnu.org/licenses/>
4
5 appendu() {
6 local help="Usage: appendu [OPTION]... FILE [LINE_SET]...
7
8 Append unique.
9
10 A LINE_SET is one or more lines. Append LINE_SET to FILE if it does not exist in
11 FILE. If no LINE_SET argument is given, read lines from stdin, and treat each
12 as a single LINE_SET. Appended text is output to the terminal.
13
14 -s don't try to use sudo when it would help us read or write the file
15 -- stop processing arguments
16 --help display this message"
17
18 local readsudo writesudo x strings string content
19 local dosudo=true
20
21 while true; do
22 if [[ $1 == --help ]]; then
23 echo "$help"
24 return
25 elif [[ $1 == -s ]]; then
26 dosudo=false
27 shift
28 elif [[ $1 == -- ]]; then
29 shift
30 break
31 else
32 break
33 fi
34 done
35
36 if [[ ${#@} == 0 ]]; then
37 echo "error: need 1 or more arguments"
38 echo "$help"
39 return 1
40 fi
41
42 local file="$1"
43 shift
44
45 local file_exists=false
46 if [[ -e $file ]]; then
47 file_exists=true
48 [[ -r $file ]] || readsudo=sudo
49 [[ -w $file ]] || writesudo=sudo
50 else
51 local dir="$(dirname "$file")"
52 if [[ -d $dir ]]; then
53 [[ ! -w $dir ]] && writesudo=sudo
54 else
55 echo "appendu error: $dir does not exist"
56 return 1
57 fi
58 fi
59 if ! $dosudo; then
60 readsudo=
61 writesudo=
62 fi
63
64 if (( $# == 0 )); then
65 unset IFS
66 while read -r x; do
67 strings+=( "$x" )
68 done
69 else
70 strings=( "$@" )
71 fi
72
73 if $file_exists; then
74 # fix files with no newline at the end.
75 # the following command won't work right on them.
76 # e = run script, $a\ means append following text, but there is none,
77 # so sed only does what it always does when it was supposed to modify a file,
78 # which is append a newline if there was none.
79 sed -ie '$a\' "$file"
80 # command substitution removes any trailing newlines, so we have to add
81 # a non-newline ending, we randomly chose "b", then remove it.
82 content=$($readsudo cat "$file"; echo b) content=${content%b}
83
84 # we aren't using regex because we want to match strings,
85 # but we also want our match to start at the beginning of a line,
86 # or the beginning of the file, and to end at a line ending.
87 # So we do some slick bash to match this.
88 local start="?(*
89 )"
90 local end="
91 *"
92 for string in "${strings[@]}"; do
93 [[ $content != $start"$string"$end ]] && $writesudo tee -a "$file"<<<"$string"
94 done
95 else
96 for string in "${strings[@]}"; do
97 $writesudo tee -a "$file"<<<"${strings[@]}"
98 done
99 fi
100 return 0
101 }