#!/bin/bash
# upc: Universal Product Code database version: 2013-09-13 14:19:46+00:00
# /usr/local/bin/upc
# http://crystalfaeries.net/posix/bin/upc
# by celeste:crystalfaery 2011-02-07 06:47:39+00:00
# designed for use with a UPC scanner that outputs "UPC#\n"
# Usage: run from cron periodically to clean the UPC database:
# 00 3 * * * /usr/local/bin/upc -c # clean up UPC database
URL="http://www.upcdatabase.com/item/" # From whence doth we scrape screens
UPCdir="/usr/local/share/upc" # in what directory do we keep the UPC database?
# upc.number.txt # filename of entry in database directory
users="users" # group which has write access to database
umask 2 # group writable is presumed, others only read it
let exitval=1 # default return for traps
appname="$(basename $0)" # who am i?
tmpfile="$(mktemp ${appname}.XXXXXX)" || exit $exitval # what is my temporary file?
trap "rm -f $UPCdir/$tmpfile; exit $exitval" 0 1 2 15 # cleanup my temporary file on exit.
####### BEWARE thou intrepid hackers, this script be recursive!
title="UPC/EAN-13 Issuing_Country Size+Weight Description"
# initialize booleans to false except the default of list mode
let help=1 # are we helping user understand how to use us
let install=1 # are we installing this software and its database
let clean=1 # are we cleaning a record or the whole database
let delete=1 # are we deleting a record or a selected set thereof
let edit=1 # are we editing a record or a selected set thereof
let list=0 # default: listing a record or a set of records
let titles=1 # displaying titles implies listing
let quit=1 # quit scanning in a loop
let scan=1 # are we scanning in a loop
# parse control flags, all of which must precede query string
query="" #initialze loop
while [ "$query" == "" ] ; do
case "$1" in
-c )
let clean=0
let list=1 # clean instead of list
shift
;;
-d )
let delete=0
let list=1 # delete instead of list
shift
;;
-e )
let edit=0
let list=1 # edit instead of list
shift
;;
-h )
let help=0
#let list=1 # help instead of list
shift
;;
-l )
let list=(! $list) # list is default
shift
;;
-q )
let quit=0
let list=1 # quit scan instead of list
shift
;;
-s )
let scan=0
let list=1 # start scan instead of list
shift
;;
-t )
let titles=(! $titles)
let list=$titles # titles imply listing
shift
;;
# spec all records, the previously selected record, or a specific record, or a search string
* )
query="$*" # we ran out of flags to parse
if [ "$query" = "" ]
then # null argument
let upc=1 # the query is not a valid UPC
previous="9999999999999" # is our special code meaning all UPCs, itself not a valid UPC
number="9999999999999" # is our special code meaning all UPCs, itself not a valid UPC
query="*" # is our special code meaning all records in our database
else # we have a query beyond all the flags
number=$(echo "$query" | tr -d -c '[:digit:]') # sanitize input for numerics
if [ "$number" = "" ] # parse the number
then # query is a non numeric string
let upc=1 # the query is not a valid UPC
previous="9999999999999" # is our special code meaning all UPCs, itself not a valid UPC
else # there are numbers in the query
if [ "$(echo $number | wc -c)" == 14 ]
then
let upc=0 # the number is a valid EAN/UCC-13
previous="$number" # save it for future reference
elif [ "$(echo $number | wc -c)" == 13 ]
then
let upc=0 # the number is a valid UPC-12
previous="$number" # save it for future reference
elif [ "$(echo $number | wc -c)" == 9 ]
then
let upc=0 # the number is a valid UPC-8
previous="$number" # save it for future reference
else # query is a text string
let upc=1 # the number is not a valid UPC even though numbers are included
previous="9999999999999" # is our special code meaning all UPCs, itself not a valid UPC
number="9999999999999" # is our special code meaning all UPCs, itself not a valid UPC
fi
fi
if [ "$query" = "*" ] # if wildcard specified
then
number="9999999999999" # is our special code meaning all UPCs, itself not a valid UPC
fi
fi
;;
esac
if [ "$number" == "9999999999999" ]
then # special marker is not an actual UPC
let upc=1 # not a UPC
query="*" # query all records
fi
if [ "$query" == "9999999999999" ]
then # special marker is not an actual UPC
let upc=1 # not a UPC
query="*" # query all records
fi
done
if [ "$help" == "0" ] # help
then
let help=1 # count our echoes before they're hatched
echo appname=$appname
echo "Usage: all flags / switches preceede the query string:"
echo "all UPCs in the query string preceede any textual argument."
echo "all UPCs in the query must be either a UPC-8 or UPC-12, to be found in web database."
echo "the pseudo-UPC 9999999999999 in the query specifies all UPCs in the local database."
echo "a null argument or "*" argument specifies all UPCs in the local database."
echo "any text following the last valid UPC is treated as a text string for alphanumeric pattern matching."
echo
echo "You will be prompted on stderr for missing data, updating the local UPC records."
echo
echo "Example: to log a titled series of UPCs to /tmp/upc.txt you might invoke:"
echo "upc -s -t | tee /tmp/upc.txt"
echo "when through scanning UPCs, type in a "-q\<enter\>" to quit."
echo
echo "------- togleable flags -------"
echo "-c to clean the entire database."
echo "-c UPC to clean record UPC in the database."
echo -n clean=
if [ "$clean" == "0" ]
then
echo true
else
echo false
fi
echo "-d to delete the database record last accessed."
echo "-d UPC to delete the database record UPC."
echo -n delete=
if [ "$delete" == "0" ]
then
echo true
else
echo false
fi
echo "-e to edit the database record last accessed."
echo "-e UPC to edit the database record UPC."
echo -n edit=
if [ "$edit" == "0" ]
then
echo true
else
echo false
fi
echo "* to list the database kept locally."
echo "UPC to list the database record UPC."
echo -n list=
if [ "$list" == "0" ]
then
echo true
else
echo false
fi
echo "-q quit scanning in a loop"
echo -n quit=
if [ "$quit" == "0" ]
then
echo true
else
echo false
fi
echo "-s start scanning in a loop, until we see the quit flag"
echo -n scan=
if [ "$scan" == "0" ]
then
echo true
else
echo false
fi
echo ------- arguments -------
echo query=$query
echo currency_argument_count=$#
echo currency_argument_first=$1
echo currency_arguments=$*
echo number=$number
echo -n upc=
if [ $upc -eq 0 ]
then
echo true
else
echo false
fi
echo ======= ======= ======= ======= ======= ======= ======= ======= ======= =======
echo -t display titles before data
if [ "$titles" == "0" ]
then
echo "$title"
fi
fi
# if we can not write the designated UPCdir, then we use /tmp
if [ -d "$UPCdir" ] # does the designated database directory exist?
then
echo -n "" > /dev/null # nop
else
echo "$UPCdir has not yet been created... will attempt installation." > /dev/stderr
let install=0 # try to complete installation+configuration
fi
cd "$UPCdir" # is the designated database directory connectable?
error=$?
if [ $error -ne 0 ]
then
echo "cd $UPCdir failed with an error code of $error" > /dev/stderr
let install=0 # try to complete installation+configuration
fi
touch "$UPCdir"/"$tmpfile" # is the designated database directory writeable?
error=$?
if [ $error -ne 0 ]
then
echo "$UPCdir is not writeable by me: error code $error" > /dev/stderr
let install=0 # try to complete installation+configuration
fi
if [ $install -eq 0 ] # install the database
then
echo This is $0 attempting to install+configure myself. > /dev/stderr
echo I want to use $UPCdir for the system-wide database. > /dev/stderr
echo I will request administrative privileges only if necessary. > /dev/stderr
mkdir -p "$UPCdir" || sudo mkdir -p "$UPCdir" || let exitval=-1 # create the directory
chmod 2775 "$UPCdir" || sudo chmod 2775 "$UPCdir" || let exitval=-1 # make it group writable world readable
chgrp "$users" "$UPCdir" || sudo chgrp "$users" "$UPCdir" || let exitval=-1 # make it group users as our best guess
if [ $exitval -eq -1 ]
then
UPCdir="/tmp" # use /tmp which is world writable
echo UPCdir=$UPCdir
let exitval=1
fi
# if we can not write the designated UPCdir, then we use /tmp
if [ -d "$UPCdir" ] # does the designated database directory exist?
then
echo -n "" > /dev/null # nop
else
echo "$UPCdir has not yet been created... will attempt installation." > /dev/stderr
UPCdir="/tmp" # use /tmp which is world writable
echo UPCdir=$UPCdir
let exitval=1
fi
cd "$UPCdir" # is the designated database directory connectable?
error=$?
if [ $error -ne 0 ]
then
echo "cd $UPCdir failed with an error code of $error" > /dev/stderr
UPCdir="/tmp" # use /tmp which is world writable
echo UPCdir=$UPCdir
let exitval=1
fi
touch "$UPCdir"/"$tmpfile" # is the designated database directory writeable?
error=$?
if [ $error -ne 0 ]
then
echo "$UPCdir is not writeable by me: error code $error" > /dev/stderr
let exitval=$error
exit $exitval # we can not even write /tmp
else
echo UPCdir=$UPCdir
cd "$UPCdir" || exit $exitval # verify I can connect into the directory
echo tmpfile=$tmpfile
fi
crontab -l > /tmp/upc.crontab.$$
echo '00 3 * * * /usr/local/bin/upc -c' >> /tmp/upc.crontab.$$
echo "Install updated crontab to clean the database nightly?:" > /dev/stderr
cat /tmp/upc.crontab.$$ > /dev/stderr
echo -n "Install updated crontab to clean the database nightly?:" > /dev/stderr
read update # what say you?
if [ "$update" == "y" ]
then
crontab /tmp/upc.crontab.$$
echo "crontab updated." > /dev/stderr
else
if [ "$update" == "yes" ]
then
crontab /tmp/upc.crontab.$$
echo "crontab updated." > /dev/stderr
else
echo "crontab remains unchanged:" > /dev/stderr
crontab -l > /dev/stderr
fi
fi
fi
# clean either the whole database or just the UPC# requested.
if [ $clean -eq 0 ]
then #Usage: run from cron periodically to clean the UPC database:
# 00' '3' '*' '*' '*' '/usr/local/bin/upc -c # clean up UPC database
find "$UPCdir" -iname "upc.*.txt" -size 0 -exec rm {} \; # remove null records
# remove null lines from record(s)
if [ $upc -eq 0 ]
then
grep '^$' upc."$number".txt > "$UPCdir"/"$tmpfile" # find records containing null lines
else
grep '^$' upc."*$query*".txt > "$UPCdir"/"$tmpfile" # find records containing null lines
fi
for file in $(sed 's/:.*$//' < "$UPCdir"/"$tmpfile") ; do # iterate list of file names
grep -v '^$' "$file" > "$UPCdir"/"$tmpfile" # remove null lines from the file
grep -v 'Last Modified' "$UPCdir"/"$tmpfile" > "$file" # yay, we saved at least one byte :-)
# linux file modification times should suffice for most users, else uncomment the next line
# echo Last Modified' '`/usr/local/bin/now`>> "$file" # yep, we touched the file
done
# remove "Item Record" lines from record(s)
if [ $upc -eq 0 ]
then
grep '^Item Record' upc."$number".txt > "$UPCdir"/"$tmpfile" # find records containing string
else
grep '^Item Record' upc."*$query*".txt > "$UPCdir"/"$tmpfile" # find records containing string
fi
for file in $(sed 's/:.*$//' < "$UPCdir"/"$tmpfile") ; do # iterate list of file names
grep -v '^Item Record' "$file" > "$UPCdir"/"$tmpfile" # remove those lines from the file
grep -v 'Last Modified' "$UPCdir"/"$tmpfile" > "$file" # yay, we saved 12 bytes :-)
# linux file modification times should suffice for most users, else uncomment the next line
# echo Last Modified' '`/usr/local/bin/now`>> "$file" # yep, we touched the file
done
# remove "Pending Requests" lines from record(s)
if [ $upc -eq 0 ]
then
grep '^Pending Requests' upc."$number".txt > "$UPCdir"/"$tmpfile" # find records containing string
else
grep '^Pending Requests' upc."*$query*".txt > "$UPCdir"/"$tmpfile" # find records containing string
fi
for file in $(sed 's/:.*$//' < "$UPCdir"/"$tmpfile") ; do # iterate list of file names
grep -v '^Pending Requests' "$file" > "$UPCdir"/"$tmpfile" # remove those lines from the file
grep -v 'Last Modified' "$UPCdir"/"$tmpfile" > "$file" # yay, we saved 19 bytes :-)
# linux file modification times should suffice for most users, else uncomment the next line
# echo Last Modified' '`/usr/local/bin/now`>> "$file" # yep, we touched the file
done
fi
# OK, now we must use our parameters to select the set of (now cleaned) records to operate upon
if [ $upc -eq 0 ]
then # we have a valid UPC number
if [ "$query" == "" ]
then
grep " " upc."$number".txt > "$UPCdir"/"$tmpfile" # find records containing string
else
grep "$query" upc."$number".txt > "$UPCdir"/"$tmpfile" # find records containing string
fi
else # we either select last or all records
if [ "$number" == "9999999999999" ]
then # all records
if [ "$query" == "" ]
then
grep " " upc.*.txt > "$UPCdir"/"$tmpfile" # find records containing string
else
grep "$query" upc.*.txt > "$UPCdir"/"$tmpfile" # find records containing string
fi
else # previous record
echo number=$number
number="$previous"
echo query=$query
echo currency_argument_count=$#
echo currency_argument_first=$1
echo currency_arguments=$*
echo number=$number
let exitval=1
exit $exitval
fi
fi
echo =======
cat "$UPCdir"/"$tmpfile"
echo =======
cut -f 2- < "$UPCdir"/"$tmpfile"| sort -u
echo =======
let exitval=1
exit $exitval
for file in $(sed 's/:.*$//' < "$UPCdir"/"$tmpfile") ; do # iterate list of file names
grep -v '^Pending Requests' "$file" > "$UPCdir"/"$tmpfile" # remove those lines from the file
grep -v 'Last Modified' "$UPCdir"/"$tmpfile" > "$file" # yay, we saved 19 bytes :-)
# linux file modification times should suffice for most users, else uncomment the next line
# echo Last Modified' '`/usr/local/bin/now`>> "$file" # yep, we touched the file
done
#######################################################################################################################
echo end of debugged code... exiting before I create a disaster or catastrophe ####### ####### ####### ####### #######
exit $exitval # failure is not an option, but the code is incomplete ####### ####### ####### ####### #######
#######################################################################################################################
# titles
if [ "$1" == "-t" ]
then
shift
echo EAN/UCC-13' 'Issuing_Country' 'Size+Weight' 'Description
fi
# delete / remove
if [ "$1" == "-d" ]
then
shift
query="$1"
if [ "$query" == "" ]
then # list all upcs we have locally
echo delete / remove wants a keyword or UPCnumber as argument, from this list:
for record in $(ls upc.[0-9]*.txt | sed 's/upc.//' | sed 's/.txt//')
do # recursive programming is fun :-)
upc "$record" # list next upc
done
else # list only matching search
for record in $(ls upc.[0-9]*.txt | sed 's/upc.//' | sed 's/.txt//')
do # recursive programming is fun :-)
contents=$(upc "$record" | grep -i "$query") # list next upc
if [ "$contents" == "" ]
then
echo -n "" > /dev/null 2>&1 # nop
else
echo "$contents"
rm -i "$UPCdir"/upc."$record".txt # delete this record?
fi
done
fi
let exitval=0
exit $exitval
fi
# list
if [ "$1" == "-l" ]
then
shift
query="$1"
if [ "$query" == "" ]
then # list all upcs we have locally
for record in $(ls upc.[0-9]*.txt | sed 's/upc.//' | sed 's/.txt//')
do # recursive programming is fun :-)
upc "$record" # list next upc
done
else # list only matching search
for record in $(ls upc.[0-9]*.txt | sed 's/upc.//' | sed 's/.txt//')
do # recursive programming is fun :-)
upc "$record" | grep -i "$query" # list next upc
done
fi
let exitval=0
exit $exitval
fi
# edit
if [ "$1" == "-e" ]
then
shift
query=`echo "$1" | tr -d -c '[:digit:]'` # sanitize input
if [ "$query" == "" ]
then
vi "$UPCdir"/upc.`cat "$UPCdir"/"$tmpfile"`.txt # edit last upc
else
vi "$UPCdir"/upc."$query".txt # edit query upc
fi
let exitval=0
exit $exitval
fi
# scan
if [ "$1" == "-s" ]
then
shift
query=`echo "$1" | tr -d -c '[:digit:]'` # sanitize input
if [ "$query" == "" ]
then
echo $title
while echo -n '>' > /dev/stderr ; do
read upc
shift
query=`echo "$1" | tr -d -c '[:digit:]'` # sanitize input for numerics
if [ "$upc" == "rm" ]
then
upc -d "$1" # deleting we allow non numeric
continue
fi
if [ "$upc" == "d" ]
then
upc -d "$1" # deleting we allow non numeric
continue
fi
if [ "$upc" == "delete" ]
then
upc -d "$1" # deleting we allow non numeric
continue
fi
if [ "$upc" == "l" ]
then
upc -l "$1" # here we allow non numeric
continue
fi
if [ "$upc" == "list" ]
then
upc -l "$1" # here we allow non numeric
continue
fi
if [ "$query" == "" ]
then
query=`cat "$UPCdir"/upc.$$.txt` # now that is a global
fi
if [ "$upc" == "e" ]
then
upc -e "$query"
continue
fi
if [ "$upc" == "edit" ]
then
upc -e "$query"
continue
fi
if [ "$upc" == "title" ]
then
echo EAN/UCC-13' 'Issuing_Country' 'Size+Weight' 'Description
fi
if [ "$upc" == "t" ]
then
echo EAN/UCC-13' 'Issuing_Country' 'Size+Weight' 'Description
fi
if [ "$upc" == "?" ]
then
echo e\(dit\), t\(itle\), q\(uit\), \(e\)x\(it\)
echo EAN/UCC-13' 'Issuing_Country' 'Size+Weight' 'Description
fi
if [ "$upc" == "h" ]
then
echo e\(dit\), t\(itle\), q\(uit\), \(e\)x\(it\)
echo EAN/UCC-13' 'Issuing_Country' 'Size+Weight' 'Description
fi
if [ "$upc" == "help" ]
then
echo e\(dit\), t\(itle\), q\(uit\), \(e\)x\(it\)
echo EAN/UCC-13' 'Issuing_Country' 'Size+Weight' 'Description
fi
if [ "$upc" == "exit" ]
then
let exitval=0
exit $exitval # quit
fi
if [ "$upc" == "x" ]
then
let exitval=0
exit $exitval # quit
fi
if [ "$upc" == "quit" ]
then
let exitval=0
exit $exitval # quit
fi
if [ "$upc" == "q" ]
then
let exitval=0
exit $exitval # quit
fi
upc "$query"
done
exit $exitval #if you got here, somebody recoded the infinite loop
else
echo EAN/UCC-13' 'Issuing_Country' 'Size+Weight' 'Description
upc "$query" # lookup the queried number and then
upc -s # continue scanning
fi
fi
# Default the Queried UPC to previous query else 0
query=`echo "$1" | tr -d -c '[:digit:]'` # sanitize input
if [ "$query" == "" ]
then
query="$(cat $UPCdir/upc.$$.txt | tr -d -c '[:digit:]')" # sanitize input######################################
if [ "$query" == "" ]
then
query="0000000000000"
fi
fi
# Use local database if we have the record, else download it
if [ -f "$UPCdir"/upc."$query".txt ]
then # Use local copy
echo "$UPC" > "$UPCdir"/upc.$$.txt
else # Fetch and Parse Item Data ##############################
curl --silent "$URL""$query" | \
lynx -dump -stdin | \
sed -n '/Item Record/,/Pending Requests/p' | \
sed 's/^ //' | \
sed 's/ / /' | \
sed 's/ / /' | \
sed 's/ / /' | \
sed 's/ / /' | \
sed 's/ / /' | \
sed 's/ / /' > "$UPCdir"/upc."$query".txt
fi
# Extract the fields of data we handle, ignoring others, prompting for missing data
# Cannonicalize the Mark of The Beast (http://www.crystalfaeries.net/video/ogv/matrix.xp2ubuntu.ogv)
# oops, there is a UCC-8 as well as UCC-13 in some records from webserver which
# we were not expecting when we coded the greps for EAN,
# because test samples did not contain those :-( 2011-02-06 15:09:26+00:00
UPC=`echo "$query" | tr -d -c '[:digit:]'` # sanitize input
EAN=$(echo -n "$(grep EAN "$UPCdir"/upc."$query".txt | cut -f 2-)")
if [ "$EAN" == "" ]
then
if [ "$UPC" == "" ]
then
UPC=`echo "$query" | tr -d -c '[:digit:]'` # sanitize input
grep "UPC" "$UPCdir"/upc."$query".txt > /dev/null
if [ $? = 0 ]
then
grep -v "UPC" "$UPCdir"/upc."$query".txt | head -n 1 > "$UPCdir"/"$tmpfile"
mv "$UPCdir"/"$tmpfile" "$UPCdir"/upc."$query".txt
fi
echo "UPC"' '"$UPC" >> "$UPCdir"/upc."$query".txt
fi
EAN="$UPC"
grep "EAN" "$UPCdir"/upc."$query".txt > /dev/null
if [ $? = 0 ]
then
grep -v "EAN/UCC-13" "$UPCdir"/upc."$query".txt > "$UPCdir"/"$tmpfile"
mv "$UPCdir"/"$tmpfile" "$UPCdir"/upc."$query".txt
fi
echo "EAN/UCC-13"' '"$EAN" >> "$UPCdir"/upc."$query".txt
fi
Country=$(echo -n "$(grep Country "$UPCdir"/upc."$query".txt | cut -f 2-)")
if [ "$Country" == "" ]
then
Country="Ter-Ra-Global"
echo "$EAN"' '"$Country" > /dev/stderr
echo -n "Issuing_Country?:" > /dev/stderr
read Country
if [ "$Country" == "" ]
then
Country="Ter-Ra-Global"
fi
grep "Issuing_Country" "$UPCdir"/upc."$query".txt > /dev/null
if [ $? = 0 ]
then
grep -v "Issuing_Country" "$UPCdir"/upc."$query".txt > "$UPCdir"/"$tmpfile"
mv "$UPCdir"/"$tmpfile" "$UPCdir"/upc."$query".txt
fi
echo "Issuing_Country"' '"$Country" >> "$UPCdir"/upc."$query".txt
fi
# Giant Economy Family Super Economy Sized
Size_Weight=$(echo -n "$(grep Size+Weight "$UPCdir"/upc."$query".txt | cut -f 2-)")
if [ "$Size_Weight" == "" ]
then
Size_Weight="1 unit"
echo "$EAN"' '"$Country"' '"$Description"' '"$Size_Weight" > /dev/stderr
echo -n "Size+Weight?:" > /dev/stderr
read Size_Weight
if [ "$Size_Weight" == "" ]
then
Size_Weight="1 unit"
fi
grep "Size+Weight" "$UPCdir"/upc."$query".txt > /dev/null
if [ $? = 0 ]
then
grep -v "Size+Weight" < "$UPCdir"/upc."$query".txt > "$UPCdir"/"$tmpfile"
mv "$UPCdir"/"$tmpfile" "$UPCdir"/upc."$query".txt
fi
echo "Size+Weight"' '"$Size_Weight" >> "$UPCdir"/upc."$query".txt
fi
# Stuff... we got stuff
Description=$(echo -n "$(grep Description "$UPCdir"/upc."$query".txt | cut -f 2-)")
if [ "$Description" == "" ]
then
Description="Unknown_Product"
echo "$EAN"' '"$Country"' '"$Description" > /dev/stderr
echo -n "Description?:" > /dev/stderr
read Description
if [ "$Description" == "" ]
then
Description="Unknown_Product"
fi
grep "Description" "$UPCdir"/upc."$query".txt > /dev/null
if [ $? = 0 ]
then
grep -v "Description" "$UPCdir"/upc."$query".txt > "$UPCdir"/"$tmpfile"
mv "$UPCdir"/"$tmpfile" "$UPCdir"/upc."$query".txt
fi
echo "Description"' '"$Description" >> "$UPCdir"/upc."$query".txt
fi
# Output the tab delimited line
echo "$EAN"' '"$Country"' '"$Description"' '"$Size_Weight"
# Celebrate another lookup
let exitval=0 # failure is not an option
exit $exitval
############### cron results ###############
grep: upc.***.txt: No such file or directory
grep: upc.***.txt: No such file or directory
grep: upc.***.txt: No such file or directory
=======
=======
=======
syntax highlighted by Code2HTML, v. 0.9.1