forked from adamlaska/datatracker
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathupdate
More file actions
executable file
·229 lines (191 loc) · 7 KB
/
update
File metadata and controls
executable file
·229 lines (191 loc) · 7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#!/bin/bash
version="0.34"
program=$(basename $0)
NEW="" # If there are more than $NEW % new lines, skip update
OLD="" # If there are more than $OLD % deleted lines, skip update
FILE=""
verbose=""
silent=""
# ----------------------------------------------------------------------
function usage() {
cat <<EOF
NAME
$program - conditionally update target file.
SYNOPSIS
$program [OPTIONS] FILE
DESCRIPTION
$program reads input from a pipe or file and saves it to a target
(FILE) if there are changes. If the new content is the same as the
old, the target is left untouched. By default, the target is also
left untouched if the new content is empty. There are options to
also abstain from applying an update if the changes are too large,
and to back up the previous version.
The purpose is to handle files with dynamically generated content in
such a manner that timestamps don't change if the content doesn't change,
and mistakes in content generation doesn't unnecessarily propagate to
the target.
OPTIONS
EOF
if [ "$(uname)" = "Linux" ]; then
egrep "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" $0 | tr -s "\t|" "\t," | sed -r -e 's/\)[ \t]+([A-Z]+)="\$2"[^#]*#/=\1\t/' -e 's/\)[^#]*#/\t/'
else
egrep "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" $0 | sed 's/\|.*"\$2"[^#]*#/ /'| sed -E 's/\|.*\)[^#]*#/ /'
fi
cat <<EOF
AUTHOR
Henrik Levkowetz <henrik@levkowetz.com>
EOF
exit
}
# ----------------------------------------------------------------------
function note() {
if [ -n "$verbose" ]; then
echo -e "$program: $*"
fi
}
# ----------------------------------------------------------------------
function warn() {
[ "$QUIET" ] || echo -e "$program: $*"
}
# ----------------------------------------------------------------------
function err() {
echo -e "$program: $*" > /dev/stderr
}
# -----------------------------------------------------------------------------
function leave() {
errcode=$1; shift
if [ "$errcode" -ge "2" ]; then warn "$*"; else note "$*"; fi
if [ -f "$tempfile" ]; then rm $tempfile; fi
if [ -f "$difffile" ]; then rm $difffile; fi
if [ "$errcode" = "1" -a "$RESULT" = "0" ]; then exit 0; else exit $errcode; fi
}
# ----------------------------------------------------------------------
# Set up error trap
trap 'leave 127 "$program($LINENO): Command failed with error code $? while processing '$origfile'."' ERR
# exit with a message if a command fails
set -e
# ----------------------------------------------------------------------
# Get any options
#
# Default values
PAT="\$path\$base.%Y-%m-%d_%H%M"
RESULT="0"
QUIET=""
# Based on the sample code in /usr/share/doc/util-linux/examples/parse.bash.gz
if [ "$(uname)" = "Linux" ]; then
GETOPT_RESULT=$(getopt -o bc:ef:hn:o:p:qrvV --long backup,maxchg:,empty,file:,help,maxnew:,maxold:,prefix:,report,quiet,verbose,version -n "$program" -- "$@")
else
GETOPT_RESULT=$(getopt bc:ef:hn:o:p:qrvV "$@")
fi
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
note "GETOPT_RESULT: $GETOPT_RESULT"
eval set -- "$GETOPT_RESULT"
while true ; do
case "$1" in
-b|--backup) backup=1; shift ;; # Back up earlier versions by creating a backup file
-c|--maxchg) CHG="$2"; shift 2 ;; # Limit on percentage of changed lines
-e|--empty) empty=1; shift ;; # Permit the update to be empty (default: discard)
-f|--file) FILE="$2"; shift 2 ;; # Read input from FILE instead of standard input
-h|--help) usage; shift ;; # Show this text and exit
-n|--maxnew) NEW="$2"; shift 2 ;; # Limit on percentage of new (added) lines
-o|--maxold) OLD="$2"; shift 2 ;; # Limit on percentage of old (deleted) lines
-p|--pat*) PAT="$2"; shift 2 ;; # Backup name base ('$path$base.%Y%m%d_%H%M')
-q|--quiet) QUIET=1; shift;; # Be less verbose
-r|--result) RESULT=1; shift ;; # Return 1 if update not done
-v|--verbose) verbose=1; shift ;; # Be more verbose about what's happening
-V|--version) echo -e "$program\t$version"; exit;; # Show version and exit
--) shift ; break ;;
*) echo "$program: Internal error, inconsistent option specification." ; exit 1 ;;
esac
done
if [ $CHG ]; then OLD=$CHG; NEW=$CHG; fi
if [ $# -lt 1 ]; then echo -e "$program: Missing output filename\n"; usage; fi
origfile=$1
tempfile=$(mktemp)
difffile=$(mktemp)
if [ -e "$origfile" ]; then
cp -p $origfile $tempfile # For ownership and permissions
cat $FILE > $tempfile
[ "$FILE" ] && touch -r $FILE $tempfile
# This won't work if we don't have sufficient privileges:
#chown --reference=$origfile $tempfile
#chmod --reference=$origfile $tempfile
else
cat $FILE > $origfile
[ "$FILE" ] && touch -r $FILE $tempfile
leave 0 "Created file '$origfile'"
fi
origlen=$(wc -c < $origfile)
newlen=$(wc -c < $tempfile)
if [ $origlen = 0 -a $newlen = 0 ]; then
rm $tempfile
leave 1 "New content is identical (and void) - not updating '$origfile'."
fi
if [ $newlen = 0 -a -z "$empty" ]; then
leave 1 "New content is void - not updating '$origfile'."
fi
diff $origfile $tempfile > $difffile || [ $? -le 1 ] && true # suppress the '1' error code on differences
difflen=$(wc -l < $difffile)
if [ $difflen = 0 ]; then
leave 1 "New content is identical - not updating '$origfile'."
fi
if [ "$OLD" -o "$NEW" ]; then
if [ "$NEW" ]; then maxnew=$(( $origlen * $NEW / 100 )); fi
if [ "$OLD" ]; then maxdel=$(( $origlen * $OLD / 100 )); fi
newcount=$(grep "^> " $difffile | wc -c)
outcount=$(grep "^< " $difffile | wc -c)
delcount=$(grep "^! " $difffile | wc -c)
delcount=$(( $outcount + $delcount ))
rm $difffile
if [ "$OLD" ]; then
if [ "$delcount" -ge "$maxdel" ]; then
cp $tempfile $origfile.update
leave 2 "New content has too many removed lines ($delcount/$origlen)\n - not updating '$origfile'.\nNew content placed in '$origfile.update' instead"
fi
fi
if [ "$NEW" ]; then
if [ "$newcount" -ge "$maxnew" ]; then
cp $tempfile $origfile.update
leave 2 "New content has too many added lines ($newcount/$origlen)\n - not updating '$origfile'.\nNew content placed in '$origfile.update' instead"
fi
fi
fi
if [ "$backup" ]; then
path=${origfile%/*}
name=${origfile##*/}
base=${name%.*}
ext=${origfile##*.}
if [ "$ext" = "$origfile" ]; then
ext=""
elif [ ! "${ext%/*}" = "$ext" ]; then
ext=""
else
ext=".$ext"
fi
if [ "$path" = "$origfile" ]; then
path=""
else
path="$path/"
fi
ver=1
backfile=$(eval date +"$PAT")
backpath="${backfile%/*}"
if [ "$backpath" = "$backfile" ]; then
backpath="."
fi
if [ ! -d $backpath ]; then
if [ -e $backpath ]; then
leave 3 "The backup path '$backpath' exists but isn't a directory"
else
mkdir -p $backpath
fi
fi
while [ -e "$backfile,$ver$ext" ]; do
ver=$(( $ver+1 ))
done
note "Saving backup: $backfile,$ver$ext"
cp -p "$origfile" "$backfile,$ver$ext"
chmod -w "$backfile,$ver$ext" || true
fi
if ! mv $tempfile $origfile; then cp -p $tempfile $origfile; fi
leave 0 "Updated file '$origfile'"