X-Git-Url: https://iankelling.org/git/?a=blobdiff_plain;f=appendu-function;h=13a7d740cd51548714ce6ff28730af2a495a3ada;hb=dc0386f28d2e917f8a5a81ff66bcd26d3b029e42;hp=40a4edfa4d6ef92c33530c77cd643461ffb06ed6;hpb=7ca836a05e81aeac0758b10d220e4b618c854ea8;p=tee-unique diff --git a/appendu-function b/appendu-function index 40a4edf..13a7d74 100644 --- a/appendu-function +++ b/appendu-function @@ -3,25 +3,23 @@ # This program is under GPL v. 3 or later, see 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. - -s don't try to use sudo when it would help us read or write the file - -- stop processing arguments - --help display this message" +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. Duplicate +LINE_SETs are treated the same. + + -- stop processing arguments + [-h|--help] display this message" - local dosudo=true while true; do - if [[ $1 == --help ]]; then + if [[ $1 == --help || $1 == -h ]]; then echo "$help" return - elif [[ $1 == -s ]]; then - dosudo=false - shift elif [[ $1 == -- ]]; then shift break @@ -36,35 +34,72 @@ Appended lines are output to the terminal. return 1 fi - local readsudo writesudo local file="$1" shift - + + local new_file=true if [[ -e $file ]]; then - [[ -r $file ]] || readsudo=sudo - [[ -w $file ]] || writesudo=sudo - else + new_file=false + else local dir="$(dirname "$file")" - if [[ -d $dir ]]; then - [[ ! -w $dir ]] && writesudo=sudo - else + if [[ ! -d $dir ]]; then echo "appendu error: $dir does not exist" - exit 1 + return 1 fi fi - if ! $dosudo; then - 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 + + local strings line + 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" + while read -r line; do + strings+=( "$line" ) done + else + strings=( "$@" ) + fi + + if ! $new_file; then + if [[ ! -r $file ]]; then + echo "appendu error: cannot read or write $file" + return 1 + fi + if [[ ! -w $file ]]; then + echo "appendu error: cannot read or write $file" + return 1 + fi + # fix files with no newline at the end. + # the following command won't work right on them otherwise. + # 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. + local content=$(cat "$file"; echo b) + content=${content%b} fi + + local reset_extglob=false + ! shopt extglob >/dev/null && reset_extglob=true + shopt -s extglob + # 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=" +*" + local return_code string return_code + for string in "${strings[@]}"; do + if $new_file || [[ $content != $start"$string"$end ]]; then + if ! tee -a "$file"<<<"$string"; then + return_code=$? + echo "appendu error: error writing to $file" + return $return_code + fi + fi + done + $reset_extglob && shopt -u extglob + return 0 }