I have written the script, which unzips a zip file (passed as argument to the bash), loops through directory and files, then perform different actions based on directory or file. It is working fine.
However I would like to know some things:
- Is it possible to do this without the unzip command? I somehow feel like that step can be avoided and we can directly use the zip file to perform the task.
- I have used three temporary files/directories(tmp.txt - to store the directory and file lines, out.xml - for the curl command, DIREC - for finding the files and directories). I have removed those three files at the end of the script. Is there a way to optimize this? may be using variables instead of files.
Here is the bash script, with comments:
#!/bin/bash
#The below line unzips the zip file as passed as argument
unzip 1ドル
length=${#1}
const=4
count=$((length-const))
DIREC=$(echo 1ドル | cut -b 1-$count)
OUTPUTFIL=out.xml
#The below line will find the directory, which was created by the unzip command. Creation of tmp.txt here.
find $DIREC -exec echo {} \; >tmp.txt
exec<tmp.txt
while read line
do
if [ -d "${line}" ] ; then
echo "$line is a directory";
else
if [ -f "${line}" ]; then
BASE=$(base64 $line);
echo "${line} is a file";
{
echo "<inpu>${BASE}</inpu>"
} > ${OUTPUTFIL}
curl -u admin:secret --data-binary @out.xml http://dp1-l2.com; echo
else
echo "${line} is not valid";
exit 1
fi
fi
done
rm -f tmp.txt
rm -rf $DIREC
rm -f out.xml
1 Answer 1
There's no documentation. Even a comment at the top of the script summarizing what it does would be better than nothing.
There's no error handling. What if the user forgets the argument?
This script will break if the argument
1ドル
contains a space. You need to write"1ドル"
in several places.You don't need to put
;
at the end of lines like this:echo "$line is a directory";
This code is really unclear:
length=${#1} const=4 count=$((length-const)) DIREC=$(echo 1ドル | cut -b 1-$count)
First, the variable names are poorly chosen:
length
of what? whyconst
?count
of what? Second, this assumes that1ドル
ends with.zip
and will go wrong if the file has any other name. Third, Bash has a feature${parameter%pattern}
for removing a suffix from a string, so you can remove the extension from1ドル
like this:DIREC=${1%.*}
But fourth and most importantly, how do you know that the ZIP file will unpack into a directory with the same name as the ZIP file? In the example below I create a ZIP file called
b.zip
that unzips into the directorya
:$ mkdir a $ touch a/a $ zip b a/a adding: a/a (stored 0%) $ rm -r a $ unzip b Archive: b.zip extracting: a/a
What you should do instead (that is, if you must unzip
1ドル
) is to use the-d
option tounzip
to extract it to a known place.$ unzip -d b b Archive: b.zip extracting: b/a/a
Why is
OUTPUTFIL
misspelled? Why not spell itOUTPUT_FILE
?It's a bad idea to put temporary files like
$OUTPUTFIL
in the current directory. There might already be a file calledout.xml
in the current directory and then you would end up overwriting it. You should use themktemp
program to create a unique temporary file name or directory, for example like this:OUTPUT_FILE=$(mktemp -t unzip-XXXXXX.xml)
These lines have several problems:
find $DIREC -exec echo {} \; >tmp.txt exec<tmp.txt while read line
First, this will go wrong if
$DIREC
contains a space: you need to use quotes. Second, this will go wrong if$DIREC
starts with a-
(it will be interpreted as an option): you need to use the-f
option tofind
. Third, there's no need to run a separateecho
command for each file:find
has a-print
option. Fourth, you don't need to write the output to a temporary file in order to read it usingwhile read line
; you can pipe the output offind
into thewhile
loop. Like this:find -f "$DIREC" -- -print | while read line; do
Inside your
while
loop you use[ -f ... ]
to ensure that you only process plain files (not directories). But you could use the-type f
argument tofind
to ensure that it only outputs plain files:find -f "$DIREC" -- -type f -print | while read line; do
This line will go wrong if
$line
contains a space or starts with a-
:BASE=$(base64 $line);
You should write:
BASE=$(base64 -i "$line")
It's not a good idea to read the output of the
base64
program into a variable like this: the output might be very large and cause Bash to use large amounts of memory. It is better to letbase64
write its output directly to your output file, so that it does not have to be stored in a variable in Bash. Instead of:BASE=$(base64 $line); { echo "<inpu>${BASE}</inpu>" } > ${OUTPUTFIL}
you should write:
{ echo "<inpu>" base64 -i "$line" echo "</inpu>" } > "${OUTPUT_FILE}"
However, there is no need for an
OUTPUT_FILE
at all here, becausecurl
accepts the filename-
meaning "standard input", so you can write:{ echo "<inpu>" base64 -i "$line" echo "</inpu>" } | curl -u admin:secret --data-binary @- http://dp1-l2.com
<inpu>
looks like a typo to me. Are you sure you don't mean<input>
? If<inpu>
is correct, you should probably add a comment to explain why.It would be better to put configuration settings like
admin:secret
andhttp://dp1-l2.com
in named variables at the top of the file. (It might be even better to accept them as command-line arguments.)The way you have written this code means that each individual file in the archive gets uploaded as a separate XML file to the server. Is this really what you want? It seems more likely to me that you would want to upload the whole archive as one XML file. Otherwise, what's the point of using XML?
As you suspected, there is in fact no need to unzip the file, because you can use
zipinfo -1
to get a listing of the contents, and then use the-p
option tounzip
to extract a file to standard output.
Putting all this together, I would write the script line this:
#!/bin/bash
# upload-zip.sh -- encode contents of ZIP archive as base64
# strings in an XML document, and upload it.
# Destination and credentials for upload.
UPLOAD_URL=http://dp1-l2.com
USER=admin
PASSWORD=secret
if [ "$#" != 1 ]; then
echo "Usage: 0ドル zipfile"
exit 1
fi
ZIPFILE=1ドル
zipinfo -1 -- "$ZIPFILE" | while read FILE; do
echo "<input>"
unzip -p -- "$ZIPFILE" "$FILE" | base64
echo "</input>"
done | curl --user "$USER:$PASSWORD" --data-binary @- --url "$UPLOAD_URL"