Copy last modified binary file over the other one - bash

I would like to compare two binary files (very small, 100Kb each) and replace the oldest with the last modified one.
I have created a simple script, but I would need your help to make it running properly:
#!/bin/sh
# select the two files
FILE1="/dir1/file1.binary"
FILE2="/dir2/file2.binary"
# create the hash of the two files
HASH1="$(md5sum $FILE1 | cut -c 1-32)"
HASH2="$(md5sum $FILE2 | cut -c 1-32)"
# compare the two hashes
if [ "$HASH1" == "$HASH2" ];
# if the two hashes are the same, exit
then
echo "the two files are identical"
exit 0
# otherwise compare which of them has been last modified
fi
DATE1="(stat -c %Y $FILE1)"
DATE2="(stat -c %Y $FILE2)"
# if FILE1 is newer than FILE2, replace FILE2 with FILE1
if [ "${DATE1}" -gt "${DATE2}" ];
then
cp $FILE1 $FILE2
echo "${FILE2} was replaced by ${FILE1}"
# if FILE2 is newer than FILE1, replace FILE1 with FILE2
fi
cp $FILE2 $FILE1
echo "${FILE1} was replaced by ${FILE2}"
exit 0
The file seems working (at least if the two files are identical), but if one file has been modified, I receive the following error:
line 24: [: {(stat -c %Y test1)}: integer expression expected
What is wrong?
By the way, is there a better way to solve this problem?
Thanks
Thank you so much everybody for your help. Here is how the script looks like now. There is also notification on QTS for QNAP, but it can be taken out if running elsewhere or not needed.
#!/bin/sh
# select the two files
FILE1="/dir1/file1"
FILE2="/dir2/file2"
# use or create a log file with timestamp of the output
LOG="/dir1/ScriptLog.txt"
TIMESTAMP=$(date +"%Y-%m-%d %Hh:%M")
if [ ! -e $LOG ]; then
touch $LOG
echo "$TIMESTAMP - INFO: '$LOG' does not exists but has been created." >&2
# else
# echo "$TIMESTAMP - INFO: '$LOG' exists and it will be used if any change to '$FILE1'
# or to '$FILE2' is needed." >&2
fi
# You can also pass the two file names as arguments for the script
if [[ $# == 2 ]]; then
FILE1=$1
FILE2=$2
fi
# check if the two files exist and are regular
if [ -f "$FILE1" -a -f "$FILE2" ]; then
# meanwhile compare FILE1 against FILE2
# if files are identical, stop there
if cmp "$FILE1" "$FILE2" 2>/dev/null>/dev/null; then
echo "$TIMESTAMP - INFO: '$FILE1' and '$FILE2' are identical." >&2 | >> $LOG
# if FILE1 is newer than FILE2, copy FILE1 over FILE2
elif [ "$FILE1" -nt "$FILE2" ]; then
if cp -p "$FILE1" "$FILE2"; then
echo "$TIMESTAMP - INFO: '$FILE1' replaced '$FILE2'." >&2 | >> $LOG
# if copy is successful, notify it into QTS
/sbin/notice_log_tool -a "$TIMESTAMP - INFO: '$FILE1' replaced '$FILE2'." --severity=5 >&2
else
echo "$TIMESTAMP - ERROR: FAILED to replace '$FILE2' with '$FILE1'." >&2 | >> $LOG
exit 1
fi
# if FILE1 is older than FILE2, copy FILE2 over FILE1
elif [ "$FILE1" -ot "$FILE2" ]; then
if cp -p "$FILE2" "$FILE1"; then
echo "$TIMESTAMP - INFO: '$FILE2' replaced '$FILE1'." >&2 | >> $LOG
# if copy is successful, notify it into QTS
/sbin/notice_log_tool -a "$TIMESTAMP - INFO: '$FILE2' replaced '$FILE1'." --severity=5 >&2
else
echo "$TIMESTAMP - ERROR: FAILED to replace '$FILE2' with '$FILE1'." >&2 | >> $LOG
exit 1
fi
# if two files are not identical but with same modification date
else
echo "$TIMESTAMP - ERROR: We should never reach this point. Something is wrong in the script." >&2 | >> $LOG
exit 1
fi
# if one file does not exist or is not valid, exit
else
echo "$TIMESTAMP - ERROR: One of the files does not exist, has been moved or renamed." >&2 | >> $LOG
# if error, notify it into QTS
/sbin/notice_log_tool -a "$TIMESTAMP - ERROR: One of the files does not exist, has been moved or renamed." --severity=5 >&2
exit 1
fi

I'm also going to suggest refactoring this, both to simplify the code, and to save your CPU cycles.
#!/bin/sh
# If both files exist....
if [ -f "$1" -a -f "$2" ]; then
# If they have the same content...
if cmp "$1" "$2" >/dev/null 2>/dev/null; then
echo "INFO: These two files are identical." >&2
# If one is newer than the other...
elif [ "$1" -nt "$2" ]; then
if cp -p "$1" "$2"; then
echo "INFO: Replaced file '$2' with '$1'." >&2
else
echo "ERROR: FAILED to replace file." >&2
exit 1
fi
# If the other is newer than the one...
elif [ "$1" -ot "$2" ]; then
if cp -p "$2" "$1"; then
echo "INFO: Replaced file '$1' with '$2'." >&2
else
echo "ERROR: FAILED to replace file." >&2
exit 1
fi
else
echo "ERROR: we should never reach this point. Something is wrong." >&2
exit 1
fi
else
echo "ERROR: One of these files does not exist." >&2
exit 1
fi
A few things that you may find useful.
This avoids calculating an md5 on each of the files. While comparing sums may be fine for small files like yours, it gets mighty expensive as your files grow. And it's completely unnecessary, because you have the cmp command available. Better to get in the habit of writing code that will work with less modification when you recycle it for the next project.
An if statement runs a command, usually [ or [[, but it can be any command. Here, we're running cmp and cp within an if, so that we can easily check the results.
This doesn't use stat anymore. While it's possible that you may never look beyond Linux, it's always a good idea to keep portability in mind, and if you can make your script portable, that's great.
This is not a bash script. Neither was your script -- if you call your script with /bin/sh, then you're in POSIX compatibility mode, which already makes this more portable than you thought. :-)
Indenting helps. You might want to adopt it for your own scripts, so that you can have a better visual idea of what commands are associated with the various conditions that are being tested.

What about something a bit simpler like the following?
#!/bin/sh
# select the two files from cli
# $1 = current file
# $2 = new file
FILE1=$1
FILE2=$2
# otherwise compare which of them has been last modified
DATE1=`(stat -c %Y $FILE1)`
DATE2=`(stat -c %Y $FILE2)`
if [ $DATE2 -gt $DATE1 ]; then
echo "cp -f $FILE2 $FILE1"
# cp -f $FILE2 $FILE1
fi

Almost there. Cleaning up your code and tweaking it a bit here is what I got
#!/bin/bash
# select the two files (default option)
FILE1="/dir1/file1.binary"
FILE2="/dir1/file2.binary"
# You can also pass the two file names as arguments for the script
if [ $# -eq 2 ]; then
FILE1=$1
FILE2=$2
fi
# create the hash of the two files
HASH1="$(md5sum $FILE1 | sed -n -e 's/^.*= //p')"
HASH2="$(md5sum $FILE2 | sed -n -e 's/^.*= //p')"
# get the dates of last modification
DATE1="$(stat -f '%m%t%Sm' $FILE1 | cut -c 1-10)"
DATE2="$(stat -f '%m%t%Sm' $FILE2 | cut -c 1-10)"
# Uncomment to see the values
#echo $FILE1 ' = hash: ' $HASH1 ' date: ' $DATE1
#echo $FILE2 ' = hash: ' $HASH2 ' date: ' $DATE2
# compare the two hashes
if [ $HASH1 == $HASH2 ]; then
# if the two hashes are the same, exit
echo "the two files are identical"
exit 0
fi
# compare the dates
if [ $DATE1 -gt $DATE2 ]; then
# if FILE1 is newer than FILE2, replace FILE2 with FILE1
cp $FILE1 $FILE2
echo "${FILE2} was replaced by ${FILE1}"
elif [ $DATE1 -lt $DATE2 ]; then
# else if FILE2 is newer than FILE1, replace FILE1 with FILE2
cp $FILE2 $FILE1
echo "${FILE1} was replaced by ${FILE2}"
else
# else the files are identical
echo "the two files are identical"
fi
Your way of getting the date was wrong, at least on my machine. So I rewrote it.
Your hash string was wrong. You were effectively cropping the string to the first 32 characters. By using sed you can actually get rid of the first part of the command and simply store the result of the md5sum.
You also misused the conditional statements as HuStmpHrrr pointed out.
The rest is cosmetics.

Related

Compare the contents of all files inside a directory

I have a directory with multiple txt files, I need to compare the contents of each txt file and print the output as "Yay all files are same" else " Oops, File are not same"
cd tmp_dir
ls
abc.txt cde.txt fgh.txt ... xyz.txt
[ tmp_dir ]$ cat abc.txt
2022-08-01_20:14:36
[ tmp_dir ]$ cat def.txt
2022-08-01_07:40:29
[ tmp_dir ]$
How to loops through files and compare the contents
for file in tmp_dir/*; do
if [ -f "$file" ]; then
cmp -s -- files # need to compare all the files under directory
fi
done
Expected Output:
#If contents are same [Output should be in Green color]
Yay, all files are same
#If contents are not same [In red color]
Oops, Files are not the same
This can be done in a single pipeline by hashing all of the files and then counting how many unique hashes you get. If the answer is 1, all of the files are the same.
distinct_hashes="$(
find dir/ -type f -exec sha512sum {} + | # hash all files in `dir/`
awk '{print $1}' | # strip file names from output
sort -u | # remove duplicate hashes
wc -l # count distinct hashes
)"
case "$distinct_hashes" in
0) echo "no files";;
1) echo "all the same";;
*) echo "not all the same";;
esac
Alternatively, you could use cmp as you tried, and it would be more efficient. You'll just have to manually loop over all of the files. Note that you don't have to compare all pairs of files, which would be O(n2); you can keep it O(n) by comparing each file to one other.
first_file=
same=1
for file in dir/*; do
[[ -f "$file" ]] || continue
if [[ -z "$first_file" ]]; then
first_file="$file"
elif ! cmp -s "$file" "$first_file"; then
same=0
break
fi
done
if [[ -z "$first_file" ]]; then
echo "no files"
elif ((same)); then
echo "all the same"
else
echo "not all the same"
fi
Advanced shell scripters might point out the quotes in distinct_hashes="$(...)", case "$distinct_hashes", [[ -f "$file" ]], [[ -z "$first_file" ]], and first_file="$file" are unnecessary. I like to include optional quotes. Quoting variable expansions is a really important habit to develop and not everyone will know the intricacies of when they are and aren't required.

Run script against a directory or a file

I have a working script that parses a text file and creates a new file from the output. How do I run this script against a single file OR a directory of files instead? Below is a general overview of the working script. Thank you for the help.
#!/usr/bin/env bash
if [ -f "$1" ]; then
*Run Some Commands against file* "$1" >> NewFile.txt
echo "Complete. Check NewFile.txt"
else
echo "Expected a file at $1, but it doesn't exist." >&2
fi
You could check if the passed argument is a directory and if so, write a loop to process the files in that directory:
#!/usr/bin/env bash
if (($# = 0)); then
echo "No arguments given" >&2
exit 2
fi
arg=$1
if [ -f "$arg" ]; then
*Run Some Commands against file* "$1" >> NewFile.txt
echo "Complete. Check NewFile.txt"
elif [ -d "$arg" ]; then
shopt -s nullglob
for file in "$arg"/*; do
# run command against "$file"
done
else
echo "Expected a file or directory as $1, but it doesn't exist." >&2
fi
An easier solution (which also recurses) is making it X-dimensional:
#!/usr/bin/env bash
if [ -d $1 ]; then
for i in $1/*; do
# start another instance of this script
$0 $1/$i
done
fi
if [ -f "$1" ]; then
*Run Some Commands against file* "$1" >> NewFile.txt
echo "Complete. Check NewFile.txt"
else
echo "Expected a file at $1, but it doesn't exist." >&2
fi

Iterating through a folder that's passed in as a paramter to a Bash script

I'm trying to iterate over a folder, running a grep on each file, and putting them into separate files, tagged with a .res extension. Here's what I have so far....
#!/bin/bash
directory=$(pwd)
searchterms="searchterms.txt"
extension=".end"
usage() {
echo "usage: fmat [[[-f file ] [-d directory ] [-e ext]] | [-h]]"
echo " file - text file containing a return-delimited list of materials"
echo " directory - directory to process"
echo " ext - file extension of files to process"
echo ""
}
while [ "$1" != "" ]; do
case $1 in
-d | --directory ) shift
directory=$1
;;
-f | --file ) shift
searchterms=$1
;;
-e | --extension ) shift
extension=$1
;;
-h | --help ) usage
exit
;;
* ) usage
exit 1
esac
shift
done
if [ ! -d "$directory" ]; then
echo "Sorry, the directory '$directory' does not exist"
exit 1
fi
if [ ! -f "$searchterms" ]; then
echo "Sorry, the searchterms file '$searchterms' does not exist"
exit 1
fi
echo "Searching '$directory' ..."
for file in "${directory}/*"; do
printf "File: %s\n" ${file}
[ -e "$file" ] || continue
printf "%s\n" ${file}
if [ ${file: -3} == ${extension} ]; then
printf "%s will be processed\n" ${file}
#
# lots of processing here
#
fi
done
I know that it's down to my poor understanding of of globbing... but I can't get the test on the extension to work.
Essentially, I want to be able to specify a source directory, a file with search terms, and an extension to search for.
NOW, I realise there may be quicker ways to do this, e.g.
grep -f searchterms.txt *.end > allchanges.end.res
but I may have other processing I need to do to the files, and I want to save them into separate files: so bing.end, bong.end, would be grep'ed into bing.end.res, bong.end.res .
Please let me know, just how stupid I'm being ;-)
Just for completeness sake, here's the last part, working, thanks to #chepner and #Gordon Davisson :
echo "Searching '$directory' ..."
for file in "${directory}"/*; do
[ -e "$file" ] || continue
# show which files will be processed
if [[ $file = *.${extension#.} ]]; then
printf "Processing %s \n" "$file"
head -n 1 "${file}" > "${file}.res"
grep -f $searchterms "${file}" >> "${file}.res"
fi
done
You just need to leave the * out of the quotes, so that it isn't treated as a literal *:
for file in "${directory}"/*; do
Unlike most languages, the quotes don't define a string (as everything in bash is already a string: it's the only data type). They simply escape each character inside the quotes. "foo" is exactly the same as \f\o\o, which (because escaping most characters doesn't really have any effect) is the same as foo. Quoted or not, all characters not separated by word-splitting characters are part of the same word.
http://shellcheck.net will catch this, although not with the most useful error message. (It will also catch the other parameter expansions that you did not quote but should.)

Bash script loop through subdirectories and write to file without using find,ls etc

Sorry for asking this question again. I have already received answer but with using find but unfortunately I need to write it without using any predefined commands.
I am trying to write a script that will loop recursively through the subdirectories in the current directory. It should check the file count in each directory. If file count is greater than 10 it should write all names of these file in file named "BigList" otherwise it should write in file "ShortList". This should look like:
---<directory name>
<filename>
<filename>
<filename>
<filename>
....
---<directory name>
<filename>
<filename>
<filename>
<filename>
....
My script only works if subdirectories don't include subdirectories in turn.
I am confused about this because it doesn't work as I expect.
Here is my script
#!/bin/bash
parent_dir=""
if [ -d "$1" ]; then
path=$1;
else
path=$(pwd)
fi
parent_dir=$path
loop_folder_recurse() {
local files_list=""
local cnt=0
for i in "$1"/*;do
if [ -d "$i" ];then
echo "dir: $i"
parent_dir=$i
echo before recursion
loop_folder_recurse "$i"
echo after recursion
if [ $cnt -ge 10 ]; then
echo -e "---"$parent_dir >> BigList
echo -e $file_list >> BigList
else
echo -e "---"$parent_dir >> ShortList
echo -e $file_list >> ShortList
fi
elif [ -f "$i" ]; then
echo file $i
if [ $cur_fol != $main_pwd ]; then
file_list+=$i'\n'
cnt=$((cnt + 1))
fi
fi
done
}
echo "Base path: $path"
loop_folder_recurse $path
How can I modify my script to produce the desired output?
This bash script produces the output that you want:
#!/bin/bash
bigfile="$PWD/BigList"
shortfile="$PWD/ShortList"
shopt -s nullglob
loop_folder_recurse() {
(
[[ -n "$1" ]] && cd "$1"
for i in */; do
[[ -d "$i" ]] && loop_folder_recurse "$i"
count=0
files=''
for j in *; do
if [[ -f "$j" ]]; then
files+="$j"$'\n'
((++count))
fi
done
if ((count > 10)); then
outfile="$bigfile"
else
outfile="$shortfile"
fi
echo "$i" >> "$outfile"
echo "$files" >> "$outfile"
done
)
}
loop_folder_recurse
Explanation
shopt -s nullglob is used so that when a directory is empty, the loop will not run. The body of the function is within ( ) so that it runs within a subshell. This is for convenience, as it means that the function returns to the previous directory when the subshell exits.
Hopefully the rest of the script is fairly self-explanatory but if not, please let me know and I will be happy to provide additional explanation.

How can a bash script write out a binary file, without using uudecode?

I've got a little puzzler here for the bash scripting experts... I have a bash script that needs to create a small (80 byte) binary file when it runs. The contents of the file need to be contained inside the script itself (i.e. I don't want to just package the file along with the script).
My script currently does it like this:
echo 'begin-base64 644 dummy.wav' > /tmp/dummy.uu
echo 'UklGRkgAAABXQVZFZm10IBAAAAADAAEAAHcBAADcBQAEACAAZmFjdAQAAAAAAAAAUEVBSxAAAAAB' >> /tmp/dummy.uu
echo 'AAAAQDYlTAAAAAAAAAAAZGF0YQAAAAA=' >> /tmp/dummy.uu
echo '====' >> /tmp/dummy.uu
uudecode -o /tmp/dummy.wav /tmp/dummy.uu
rm /tmp/dummy.uu
... after the above runs, I have my file /tmp/dummy.wav. But I just found out that the computer this script is to run on does not have uudecode installed (and I'm not allowed to install it), so I need to find some other way to create this file. Any ideas?
Some installers do something similar to this:
#!/bin/bash
tail -n +4 $0 | tar xvzf -
exit
<tgz file appended here><newline>
If the target computer has perl available:
perl -ne 'print unpack("u",$_)' > dummy.wav <<EOD
M4DE&1D#```!7059%9FUT(!`````#``$``'<!``#<!0`$`"``9F%C=`0`````
C````4$5!2Q`````!````0#8E3```````````9&%T80``````
EOD
That's using the non-base64 format you get from just doing uuencode dummy.wav < dummy.wav on your original computer.
Failing that, you can always do this:
echo -ne '\x52\x49\x46\x46\x48\x00\x00\x00' > dummy.wav
echo -ne '\x57\x41\x56\x45\x66\x6d\x74\x20' >> dummy.wav
echo -ne '\x10\x00\x00\x00\x03\x00\x01\x00' >> dummy.wav
echo -ne '\x00\x77\x01\x00\x00\xdc\x05\x00' >> dummy.wav
echo -ne '\x04\x00\x20\x00\x66\x61\x63\x74' >> dummy.wav
echo -ne '\x04\x00\x00\x00\x00\x00\x00\x00' >> dummy.wav
echo -ne '\x50\x45\x41\x4b\x10\x00\x00\x00' >> dummy.wav
echo -ne '\x01\x00\x00\x00\x40\x36\x25\x4c' >> dummy.wav
echo -ne '\x00\x00\x00\x00\x00\x00\x00\x00' >> dummy.wav
echo -ne '\x64\x61\x74\x61\x00\x00\x00\x00' >> dummy.wav
This little bit of bash was generated with:
$ hexdump -e '"echo -ne '\''" 8/1 "x%02x" "'\'' >> dummy.wav\n"' dummy.wav | sed 's;x;\\x;g;1s/>/ /'
Edited to add:
As pointed out in a reply here, something like this is also a possibility:
xargs -d'\n' -n1 echo -ne > dummy.wav <<EOD
\x52\x49\x46\x46\x48\x00\x00\x00\x57\x41\x56\x45\x66\x6d\x74\x20
\x10\x00\x00\x00\x03\x00\x01\x00\x00\x77\x01\x00\x00\xdc\x05\x00
\x04\x00\x20\x00\x66\x61\x63\x74\x04\x00\x00\x00\x00\x00\x00\x00
\x50\x45\x41\x4b\x10\x00\x00\x00\x01\x00\x00\x00\x40\x36\x25\x4c
\x00\x00\x00\x00\x00\x00\x00\x00\x64\x61\x74\x61\x00\x00\x00\x00
EOD
(the -d argument is important to turn off xargs's own backslash processing)
You can also turn the 8/1 in my hexdump command into 80/1 and have a single long echo line.
Put an exit at the end of your script, append the file to the end of the script, and use tail -c 80 to get at the contents. This will work as long as you don't need to worry about newline conversion issues.
This is another example to decode radix 64 formatted data, it runs slow, but it is functional.
#!/bin/bash
exec<$0
while read line ; do if [ "$line" = "#payload" ] ; then break; fi; done
r64='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
i=0; while [ $i -lt 256 ] ; do tab[$i]=-1 ; let i=$i+1 ;done
i=0; while [ $i -lt 64 ] ; do tab[`printf "%d" "'${r64:$i:1}"`]=$i ; let i=$i+1; done
bi=0
while read -n 1 x
do
in=${tab[`printf "%d" "'$x"`]}
if [ $in -ge 0 ]; then case $bi in
0 ) out=$(($in<<2)); bi=6 ;;
2 ) out=$(($out|$in)); printf \\$(printf '%03o' $(($out&255)) ); bi=0 ;;
4 ) out=$(($out+($in>>2))); printf \\$(printf '%03o' $(($out&255)) );
bi=0; out=$(($in<<6)); bi=2 ;;
* ) out=$(($out+($in>>4))); printf \\$(printf '%03o' $(($out&255)) );
bi=0; out=$(($in<<4)); bi=4 ;;
esac fi
done
exit
#payload
dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2XmAgICAte3Z2dnZ2dnZ2dnZ2dnZ2dgp2dnZ2dnZ2dnZ2dnZ2dnZ2PiAg
ICAgIC4gLXZ2dnZ2dnZ2dnZ2dnZ2CnZ2dnZ2dnZ2dnZ2dnZ2dn0gICAgICAgPT4gLXZ2dnZ2dnZ2dnZ2
dnYKdnZ2dnZJdnZJdnZJdnZJOyAgICAgICAtICAgPXZJdkl2dkl2dkl2dgp2dnZ2SXZ2dnZ2dnZ2dnZg
ICAgICAgICAgICAgbnZ2dnZJdnZ2dnZ2CnZ2dnZ2dnZ2SXZJdnZJdiAgIC4gICAgICwgICA8dnZ2SXZ2
dkl2dkkKdnZ2SXZ2SXZ2dnZ2SXZJIF9zOyAgX3VvLyAgID12dnZ2dnZ2dnZJdgp2dnZ2dkl2dnZJdnZ2
dnYgdyRtICBtQCRtICAgPXZ2dnZJdnZJdnZ2CnZ2dnZJdnZ2dnZ2dkl2SSBmPTQuO1cgYFE7ICA9dnZ2
dnZ2dnZ2dnYKdnZ2SXZ2dnZJdnZJdnZ2IHQtM3MlJiAgbWAgID12dnZ2SXZJdnZJdgp2dnZ2dnZ2SXZ2
dnZ2dnYgXWlvWjZYYXVQICAgPXZ2dnZ2dnZ2SXZ2CnZ2dkl2dkl2dnZJdnZJdi4pbVojWlojWlMgICAu
dnZ2SXZJdnZ2dnYKdnZ2dnZ2dnZ2dnZ2SXZ2OjNYWlpaI1pTWCAgICB7dnZ2dnZ2dkl2dgp2dnZ2SXZ2
SXZ2SXZ2dnY7PFNYWlhTZFhuIC5pLj12dnZJdnZJdnZ2CnZ2dkl2dnZ2dkl2dnZ2dmBdJVhYWlhubW0+
IC1gIHZ2dnZ2dnZ2dnYKdnZ2dnZ2SXZ2dnZ2SXYlIGptdklud1FXUW0gICAgPHZ2SXZ2SXZ2SQp2dnZJ
dnZ2dkl2dkl2dmAuUVFvWG1tUVFRUWMgICAge0l2dnZ2dnZ2CnZ2dnZ2dkl2dnZ2dnYrIGpRV1FtV1FR
UVFRayAgICAtdnZ2dkl2dkkKdnZ2dkl2dnZ2SXZJPiBfUVFRUVFRUVFRUVFRLiAgICA9dkl2dnZ2dgp2
dnZJdnZ2SXZ2dmwgIF1RUVFRV1FRUVdXOCRMICAgICA8dnZ2SXZ2CnZ2dnZ2dnZ2dnZ2OyAgbm1RUVFt
UVFRbXdvb20gLiAgIC1JdnZ2dnYKdnZ2SXZ2SXZ2SX0gID1RV1FRUVFRUVFRUVFtMlsgLSAgID12dkl2
dgp2dnZ2dnZ2dkl2Oy4gZFFRUVFRUVFRUVFRUVFRcSAgLiAgIEl2dnZ2CnZ2dnZJdkl2dnZgLjxRUVFR
UVFRUVFRUVFRUVdRKC4uLiAgPEl2dnYKdnZ2SXZ2dnZ2PiAgZFFRUVFRUVFRUVFRUVFRUVFbICAuICAg
dnZ2SQp2dnZ2dnZ2dnYnIC5RUVFRUVFRUVFRUVFRUVFRUWsgIC4gICB7dnZ2CnZ2dkl2dkl2PiAuXVFR
UVFRV1dXUVFRUVFRUVFRbSAgICAgIClsdnYKdnZ2dnZ2dnZgIDpqUVFRUVEjUVdRUVFRUVFRUVFXICAu
ICAgOnZ2SQp2dnZ2SXZ2bCAgOmpXUVFRUUVXV1FRUVFRV1FRUVcgIGAgICA6dnZ2CnZ2dkl2dnZJLl86
alFRUVFRRVdRUVFRUVFRUVFRVyAuIC4uID12dnYKdnZ2dnZ2dnZkIzYvUVdRUVFFUVFRUVFRUVFRV1dM
LiAgIDogKXZ2dgp2dnZJdnZJMyNaWkwtJFFRUVFRV1FRUVFRUVFCWiNgICAgLmRvdnZ2CnZ2dnZ2SXZa
IyMjWj4tNFFRUVdRUVFRUVFRUUVaay4gICBqWlh2dnYKdnZ2dndvbVgjWiNaIy4gNFFRUVFRUVFRUVdX
MVpYc189dVhaaHZ2dgp2dnZaWiNaI1VVWiNaTCAgXVFRUVFRUVFRUVdlWFpYcVhtWiNVenZ2CnZ2SVgj
I1ojWiMjWiNaLyAuUVFRUVFRUVFRVzEzI1paWlojWiMjb3YKdnZ2ZFVaIyNVWiMjVVVoX2FRUVFRUVFR
UVFQOlhaIyNVI1ojVVojaAp2dklkIyNaI1ojI1ojWlpaV1FRUVFRUVFXUCA9ZFojWiNaIyNaIyNaCnZ2
dlojWiMjVVVaI1ojWlpKUVFRUVFXUF4gIClYIyNaI1VVWiNVWjEKdnZ7WlojWlVaIyNaIyNaVXMtIT8i
fiAgICAgdlgjWiMjWiNaWF5sdgp2bCBZWFhYWFpaVVUjWlpaMS4gICAgICAgICB2WFojWiNaWCIgIDx2
CnZzICAtfiJJMVhYWFpaWm8xICAgICAgICAgIEluWFhaU31gICAgPHYKdnY7ICAgICAtLTwxMjIxbGAg
ICAgICAgICAgPElubjF9ICAgICB2SQp2dmwsICAgICAgICB+Kz5gICAgICAgICAgICAgfnwrfiAgICAu
JUl2CnZ2dnZpLiAgICAgICAgICAgIF9pc2ksICAgICAgICAgICAgX3ZJdnYKdnZ2dnZ2c19fXy4uLi5f
XyV2dnZ2SXZpLCxfLiAuLl9fPnZ2dnZ2dgp2dnZ2SXZ2dm52dnZ2dnZudnZ2dnZ2dnZubnZ2dnZ2dnZ2
dnZ2dnZ2Cg==
#!/bin/bash
# Define usage help
usage () {
echo -e "USAGE:\n\t$0 <file to create> <dir to tar> <name of script or command to run>\n"
exit 0
}
# check commandline arguments
if [ "$1" = "-h" ]; then usage; fi
if [ -z $1 ]; then usage; fi
if [ -z $2 ]; then usage; fi
if [ -z $3 ]; then usage; fi
# test for the directory and if it exists, create the bin file using tar
if [ -d "$2" ]; then
cat >$1<<EOF
#!/bin/sh -e
sed -e '1,/^exit$/d' "\$0" | tar xzf - && "./$2/$3"
exit
EOF
tar czf - $2 >> $1
else
echo "$2 does not exist, aborting!"
exit 1
fi
# make the new file executable and exit
chmod +x $1
exit 0
I would use base64 encoding, as that seems to be the general replacement for uu encoding, and operates on very similar principles.
From my point of view uuencode and uudecode are essential, but
that's only my opinion.
Without creating temporary files you could also do something
like this (uudecode.sh):
#!/bin/bash
# --
# -- Uudecoding without using a regular temporary file
# --
# -- Create a named pipe:
mknod /tmp/.dummypipe p
# -- Starting uudecoding on that pipe in the background:
uudecode -o dummy.txt /tmp/.dummypipe &
# -- Push base64-uuencoded content into the named pipe:
cat <<END_DUMMY > /tmp/.dummypipe
begin-base64 644 dummy.txt
VGhpcyBpcyB0aGUgdXVkZWNvZGVkIHRleHQuCg==
====
END_DUMMY
# -- Remove the named pipe
rm /tmp/.dummypipe
Just encode the binary data in base64 and do something like this:
#! /bin/bash
cat <<EOF | base64 -d > wherever-it-goes.bin
UtEOtUaZcUCrEJtPJrA34BH8Wdpxb1/DtfMo5syiE/h+moNyApEkg2ZwA2/jTDdfl4WijnNbMzvZ
RrF3i7X353AjmTjLBz1NcOOJJhRPYLJ4WQYONyYj/fAhnXQd+s4SHaNponOWKj1AAzdlJY1VLWaX
P8QBJQcn2FTL4pJ3N04=
EOF

Resources