#!/bin/bash # Copyright (C) 2014 Ian Kelling # This program is under GPL v. 3 or later, see appendu() { local help="Usage: appendu [OPTION]... FILE [LINE_SET]... Append unique. 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 content local dosudo=true while true; do if [[ $1 == --help ]]; then echo "$help" return elif [[ $1 == -s ]]; then dosudo=false shift elif [[ $1 == -- ]]; then shift break else break fi done if [[ ${#@} == 0 ]]; then echo "error: need 1 or more arguments" echo "$help" return 1 fi local file="$1" shift local file_exists=false if [[ -e $file ]]; then file_exists=true [[ -r $file ]] || readsudo=sudo [[ -w $file ]] || writesudo=sudo else local dir="$(dirname "$file")" if [[ -d $dir ]]; then [[ ! -w $dir ]] && writesudo=sudo else echo "appendu error: $dir does not exist" return 1 fi fi if ! $dosudo; then readsudo= writesudo= fi if (( $# == 0 )); then unset IFS while read -r x; do 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" # command substitution removes any trailing newlines, so we have to add # a non-newline ending, we randomly chose "b", then remove it. content=$($readsudo cat "$file"; echo b) content=${content%b} # 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 [[ $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 }