# This program is under GPL v. 3 or later, see <http://www.gnu.org/licenses/>
appendu() {
- local help="Usage: appendu [OPTION]... FILE [LINE]...
+ local help="Usage: appendu [OPTION]... FILE [LINE_SET]...
+
Append unique.
-Append each line to FILE if it does not exist in FILE.
-Use LINE if specified, else use lines from stdin.
-Appended lines are output to the terminal.
+
+A LINE_SET is one or more lines. Append LINE_SET to FILE if it does not exist in
+FILE. If no LINE_SET argument is given, read lines from stdin, and treat each
+as a single LINE_SET. Appended text is output to the terminal.
-s don't try to use sudo when it would help us read or write the file
-- stop processing arguments
--help display this message"
+ local readsudo writesudo x strings string
local dosudo=true
while true; do
return 1
fi
- local readsudo writesudo x
local file="$1"
shift
-
+
+ local readsudo=false
+ local file_exists=false
if [[ -e $file ]]; then
- [[ -r $file ]] || readsudo=sudo
+ file_exists=true
+ [[ -r $file ]] || readsudo=true
[[ -w $file ]] || writesudo=sudo
- else
+ else
local dir="$(dirname "$file")"
if [[ -d $dir ]]; then
[[ ! -w $dir ]] && writesudo=sudo
readsudo=
writesudo=
fi
- if (( $# )); then
- for x in "$@"; do
- [[ -e "$file" ]] && $readsudo grep -q "^$x$" "$file" || $writesudo tee -a "$file"<<<"$x"
- done
- elif [[ ! -t 0 ]]; then
+
+ if (( $# == 0 )); then
unset IFS
while read -r x; do
- # duplicated from above
- [[ -e "$file" ]] && $readsudo grep -q "^$x$" "$file" || $writesudo tee -a "$file"<<<"$x"
+ strings+=( "$x" )
+ done
+ else
+ strings=( "$@" )
+ fi
+
+ if $file_exists; then
+ # fix files with no newline at the end.
+ # the following command won't work right on them.
+ # e = run script, $a\ means append following text, but there is none,
+ # so sed only does what it always does when it was supposed to modify a file,
+ # which is append a newline if there was none.
+ sed -ie '$a\' "$file"
+ # this removes any trailing newline in the var, so we add it back on,
+ # because we want a consistent ending to match
+ local file_content
+ if $readsudo; then
+ file_content="$(sudo cat "$file")
+"
+ else
+ file_content="$(<"$file")
+"
+ fi
+ # we aren't using regex because we want to match strings,
+ # but we also want our match to start at the beginning of a line,
+ # or the beginning of the file, and to end at a line ending.
+ # So we do some slick bash to match this.
+ local start="?(*
+)"
+ local end="
+*"
+ for string in "${strings[@]}"; do
+ [[ $file_content != $start"$string"$end ]] && $writesudo tee -a "$file"<<<"$string"
+ done
+ else
+ for string in "${strings[@]}"; do
+ $writesudo tee -a "$file"<<<"${strings[@]}"
done
fi
+ return 0
}
appendu "$@"
# This program is under GPL v. 3 or later, see <http://www.gnu.org/licenses/>
appendu() {
- local help="Usage: appendu [OPTION]... FILE [LINE]...
+ local help="Usage: appendu [OPTION]... FILE [LINE_SET]...
+
Append unique.
-Append each line to FILE if it does not exist in FILE.
-Use LINE if specified, else use lines from stdin.
-Appended lines are output to the terminal.
+
+A LINE_SET is one or more lines. Append LINE_SET to FILE if it does not exist in
+FILE. If no LINE_SET argument is given, read lines from stdin, and treat each
+as a single LINE_SET. Appended text is output to the terminal.
-s don't try to use sudo when it would help us read or write the file
-- stop processing arguments
--help display this message"
+ local readsudo writesudo x strings string
local dosudo=true
while true; do
return 1
fi
- local readsudo writesudo x
local file="$1"
shift
-
+
+ local readsudo=false
+ local file_exists=false
if [[ -e $file ]]; then
- [[ -r $file ]] || readsudo=sudo
+ file_exists=true
+ [[ -r $file ]] || readsudo=true
[[ -w $file ]] || writesudo=sudo
- else
+ else
local dir="$(dirname "$file")"
if [[ -d $dir ]]; then
[[ ! -w $dir ]] && writesudo=sudo
readsudo=
writesudo=
fi
- if (( $# )); then
- for x in "$@"; do
- [[ -e "$file" ]] && $readsudo grep -q "^$x$" "$file" || $writesudo tee -a "$file"<<<"$x"
- done
- elif [[ ! -t 0 ]]; then
+
+ if (( $# == 0 )); then
unset IFS
while read -r x; do
- # duplicated from above
- [[ -e "$file" ]] && $readsudo grep -q "^$x$" "$file" || $writesudo tee -a "$file"<<<"$x"
+ strings+=( "$x" )
+ done
+ else
+ strings=( "$@" )
+ fi
+
+ if $file_exists; then
+ # fix files with no newline at the end.
+ # the following command won't work right on them.
+ # e = run script, $a\ means append following text, but there is none,
+ # so sed only does what it always does when it was supposed to modify a file,
+ # which is append a newline if there was none.
+ sed -ie '$a\' "$file"
+ # this removes any trailing newline in the var, so we add it back on,
+ # because we want a consistent ending to match
+ local file_content
+ if $readsudo; then
+ file_content="$(sudo cat "$file")
+"
+ else
+ file_content="$(<"$file")
+"
+ fi
+ # we aren't using regex because we want to match strings,
+ # but we also want our match to start at the beginning of a line,
+ # or the beginning of the file, and to end at a line ending.
+ # So we do some slick bash to match this.
+ local start="?(*
+)"
+ local end="
+*"
+ for string in "${strings[@]}"; do
+ [[ $file_content != $start"$string"$end ]] && $writesudo tee -a "$file"<<<"$string"
+ done
+ else
+ for string in "${strings[@]}"; do
+ $writesudo tee -a "$file"<<<"${strings[@]}"
done
fi
+ return 0
}