use strings, not regex, and allow multi-line args
authorIan Kelling <ian@iankelling.org>
Fri, 25 Jul 2014 09:15:18 +0000 (02:15 -0700)
committerIan Kelling <ian@iankelling.org>
Fri, 25 Jul 2014 09:15:18 +0000 (02:15 -0700)
appendu
appendu-function

diff --git a/appendu b/appendu
index 7854aee9b1c324de99f907a429e62f465a63e9b8..c18830da8662a9a22972cdf9f8cae13b0aebaaac 100755 (executable)
--- a/appendu
+++ b/appendu
@@ -3,16 +3,19 @@
 # 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
@@ -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 "$@"
index 4c97d7270adbd3f4af06d063682931c88f9309ca..7749aa47e7fafa3b29c5b158a149ba7e5995e690 100644 (file)
@@ -3,16 +3,19 @@
 # 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
@@ -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
 }