From: Ian Kelling Date: Fri, 25 Jul 2014 09:15:18 +0000 (-0700) Subject: use strings, not regex, and allow multi-line args X-Git-Url: https://iankelling.org/git/?a=commitdiff_plain;h=7f06af6d08609065f3475d5949046aa17e165813;p=tee-unique use strings, not regex, and allow multi-line args --- diff --git a/appendu b/appendu index 7854aee..c18830d 100755 --- a/appendu +++ b/appendu @@ -3,16 +3,19 @@ # 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. + +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 @@ -36,14 +39,16 @@ Appended lines are output to the terminal. 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 @@ -56,16 +61,49 @@ Appended lines are output to the terminal. 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 "$@" diff --git a/appendu-function b/appendu-function index 4c97d72..7749aa4 100644 --- a/appendu-function +++ b/appendu-function @@ -3,16 +3,19 @@ # 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. + +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 @@ -36,14 +39,16 @@ Appended lines are output to the terminal. 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 @@ -56,15 +61,48 @@ Appended lines are output to the terminal. 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 }