How can I avoid multiple starting of a bash script? - bash

I wrote a little bash script called "wp", which upload files to an ftp server. It uses the wput utility. It takes the list of files from a text file. When uploading is ready it comments out the line with a double cross in the text file. The success of the upload is detected according to the last line in the logfile. My question is how can I avoid multiple starting of my script? I am trying to detect with pgrep if the instance is running, but doesn't work correctly:
#!/bin/bash
if [ "$(pgrep ^wp$|wc -l)" -eq "2" ]
then
echo "$(pgrep ^wp$)"
echo "$(pgrep ^wp$|wc -l)"
echo "wp script is starting..."
else
echo "$(pgrep ^wp$)"
echo "$(pgrep ^wp$|wc -l)"
echo "wp script is already running!"
exit
fi
server="ftp://username:password#ftp.ftpserver.com"
logfile=~/uploads.log
listfile=~/uploads.txt
list_backup=~/uploads_bak000.txt
while read f;
do
ret=""
if [ "${f:0:1}" = "#" -o "$f"1 = 1 ]
then
if [ "$f"1 = 1 ]
then
:
#echo "invalid string: "$f
else
#first character is remark sign # then empty command -> :
echo "remark line skipped: "$f
fi
else
#while string $ret is empty
while [ -z "$ret" ]
do
wput "$f" --tries=-1 "$server" 2>&1|tee -a $logfile #> /dev/null
ret=$(tail -n 1 "$logfile"|grep "FINISHED\|Nothing\|Skipped\|Transfered")
done
if [ -n "$ret" ]
then
cat $listfile > $list_backup
awk -v f="$f" '{if ($0==f && $0!~/#/) print "#" $0; else print $0;}' $list_backup > $listfile
fi
fi
done < $listfile

There are quick-n-dirty solutions that use ps with grep (don't do this).
It is better to use a lock file as a "mutex". A nice way of doing this is by using a directory as a lock file (http://mywiki.wooledge.org/BashFAQ/045).
I would also suggest taking a look at:
http://mywiki.wooledge.org/ProcessManagement#How_do_I_make_sure_only_one_copy_of_my_script_can_run_at_a_time.3F
, which mentions use of setlock(http://cr.yp.to/daemontools/setlock.html) that abstracts the lock file handling for you.

Related

How can I pipe output, from a command in an if statement, to a function?

I can't tell if something I'm trying here is simply impossible or if I'm really lacking knowledge in bash's syntax. This is the first script I've written.
I've got a Nextcloud instance that I am backing up daily using a script. I want to log the output of the script as it runs to a log file. This is working fine, but I wanted to see if I could also pipe the Nextcloud occ command's output to the log file too.
I've got an if statement here checking if the file scan fails:
if ! sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all; then
Print "Error: Failed to scan files. Are you in maintenance mode?"
fi
This works fine and I am able to handle the error if the system cannot execute the command. The error string above is sent to this function:
Print()
{
if [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "No" ]; then
echo "$1" | tee -a "$log_file"
elif [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "Yes" ]; then
echo "$1" >> "$log_file"
elif [[ "$logging" -eq 0 ]] && [ "$quiet_mode" = "No" ]; then
echo "$1"
fi
}
How can I make it so the output of the occ command is also piped to the Print() function so it can be logged to the console and log file?
I've tried piping the command after ! using | Print without success.
Any help would be appreciated, cheers!
The Print function doesn't read standard input so there's no point piping data to it. One possible way to do what you want with the current implementation of Print is:
if ! occ_output=$(sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all 2>&1); then
Print "Error: Failed to scan files. Are you in maintenance mode?"
fi
Print "'occ' output: $occ_output"
Since there is only one line in the body of the if statement you could use || instead:
occ_output=$(sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all 2>&1) \
|| Print "Error: Failed to scan files. Are you in maintenance mode?"
Print "'occ' output: $occ_output"
The 2>&1 causes both standard output and error output of occ to be captured to occ_output.
Note that the body of the Print function could be simplified to:
[[ $quiet_mode == No ]] && printf '%s\n' "$1"
(( logging )) && printf '%s\n' "$1" >> "$log_file"
See the accepted, and excellent, answer to Why is printf better than echo? for an explanation of why I replaced echo "$1" with printf '%s\n' "$1".
How's this? A bit unorthodox perhaps.
Print()
{
case $# in
0) cat;;
*) echo "$#";;
esac |
if [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "No" ]; then
tee -a "$log_file"
elif [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "Yes" ]; then
cat >> "$log_file"
elif [[ "$logging" -eq 0 ]] && [ "$quiet_mode" = "No" ]; then
cat
fi
}
With this, you can either
echo "hello mom" | Print
or
Print "hello mom"
and so your invocation could be refactored to
if ! sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all; then
echo "Error: Failed to scan files. Are you in maintenance mode?"
fi |
Print
The obvious drawback is that piping into a function loses the exit code of any failure earlier in the pipeline.
For a more traditional approach, keep your original Print definition and refactor the calling code to
if output=$(sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all 2>&1); then
: nothing
else
Print "error $?: $output"
Print "Error: Failed to scan files. Are you in maintenance mode?"
fi
I would imagine that the error message will be printed to standard error, not standard output; hence the addition of 2>&1
I included the error code $? in the error message in case that would be useful.
Sending and receiving end of a pipe must be a process, typically represented by an executable command. An if statement is not a process. You can of course put such a statement into a process. For example,
echo a | (
if true
then
cat
fi )
causes cat to write a to stdout, because the parenthesis put it into a child process.
UPDATE: As was pointed out in a comment, the explicit subprocess is not needed. One can also do a
echo a | if true
then
cat
fi

i don't know if my shell script is correct

I have a homework using for loop but I'm not quite understand the task that I have to do in there. I wrote a script but I feel like it's not a correct script. Please help!
Here is the question:
Write a shell script to list out the contents of any directory, and indicate for each file (including invisible ones) whether the file is a directory, a plain file, and whether it is public and/or executable to this process
#!/bin/bash
if [ $# -lt 1 ] ; then
echo " file doesn't exist"
echo
echo " variable needed to run a command"
fi
echo ---------------------------------------------
echo ---------------------------------------------
for i in $*
do
if [ -f $i ]; then
echo " it's a file";
echo "THIS IS A LIST OF FILE and DIRECTORY in $i"
ls -a $i
fi
done
echo -----------------------------------------
if [ -d $i ]; then
echo "directory" ;
echo "THIS IS A LIST OF FILES AND DIRETORY in $i"
ls -a $i
fi
echo ------------------------------------------
if [ -x $i ]; then
echo "executable"
echo "THIS IS A LIST OF EXECUTABLE FILE IN $i"
ls -x $i
fi
echo -----------------------------------------
if [ -r $i ]; then
echo "this file is a public file"
else "this is a private file"
fi
#!/bin/bash
if [ $# -lt 1 ] ; then
echo " file doesn't exist"
echo
echo " variable needed to run a command"
fi
echo ---------------------------------------------
echo ---------------------------------------------
for i in $*
do
if [ -f $i ]; then
echo " it's a file";
echo "THIS IS A LIST OF FILE and DIRECTORY in $i"
ls -a $i
fi
done
echo -----------------------------------------
if [ -d $i ]; then
echo "directory" ;
echo "THIS IS A LIST OF FILES AND DIRETORY in $i"
ls -a $i
fi
echo ------------------------------------------
if [ -x $i ]; then
echo "executable"
echo "THIS IS A LIST OF EXECUTABLE FILE IN $i"
ls -x $i
fi
echo -----------------------------------------
if [ -r $i ]; then
echo "this file is a public file"
else "this is a private file"
fi
Poorly written specifications are the bane of education. "Public" sounds like the wrong word here. I'll assume it means "readable".
You check if there's an argument, but you don't exit the program if there is not. I'd also confirm it's a directory, and readable.
The manual will do you a lot of good. Expect to do a lot of reading till you learn this stuff, and then reference it a lot to be sure.
Read this section carefully, create some tests for yourself to prove they work and that you understand them, and your job will be more than half done.
Don't use [. Generally it's just better to always use [[ instead, unless you are using (( or case or some other construct.
I don't see that a for loop was specified, but it ought to be fine. Just be aware that you might have to specify $1/* and $1/.* separately.
Put all your tests in one loop, though. For each file, test for whether it's a directory - if it is, report it. Test if it's a plain file - if it is, report it.
I do NOT like doing homework for someone, but it looks like you could use an example that simplifies this. I recommend you not use this as written - break it out and make it clearer, but this is a template for the general logic.
#! /bin/env bash
(( $# )) && [[ -d "$1" ]] && [[ -r "$1" ]] || {
echo "use: $0 <dir>" >&2
exit 1
}
for e in "$1"/.* "$1"/*
do echo "$e:"
[[ -d "$e" ]] && echo " is a directory"
[[ -f "$e" ]] && echo " is a plain file"
[[ -r "$e" ]] && echo " is readable"
[[ -x "$e" ]] && echo " is executable"
done
If you read the links I provided you should be able to break this apart and understand it.
Generally, your script is long and a bit convoluted. Simpler is easier to understand and maintain. For example, be very careful about block indentation to understand scope.
$: for i in 1 2 3
> do echo $i
> done
1
2
3
$: echo $i
3
Compare this to -
for i in $*
do if [ -f $i ]; then
echo " it's a file";
echo "THIS IS A LIST OF FILE and DIRECTORY in $i"
ls -a $i
fi
done
echo -----------------------------------------
if [ -d $i ]; then
echo "directory" ;
echo "THIS IS A LIST OF FILES AND DIRETORY in $i"
ls -a $i
fi
You are testing each entry to see if it is a file, and if it is, reporting "THIS IS A LIST OF FILE and DIRECTORY in $i" every time...
but then only testing the last one to see if it's a directory, because the [ -d $i ] is after the done.
...did you run this somewhere to try it, and look at the results?

Catch the search value into the variable in unix shell script

I am trying to search content of file 1 into file 2 and if the content is found then store in found.csv file or store in notfound.csv file
Below is my code,
cd /mnt/data/dobiminer/scripts
usage="Usage:sh scriptname.sh 'ToSearchFile' 'MainSearchFile' 'CR' "
Date=`date +%m%d%y%H%M%S`
File=$(<$2)
echo "File Input $2"
echo $File
if [ $# != 3 ]
then
echo $usage
exit 1
else
echo > "$3-Found-$Date.csv"
echo > "$3-NotFound-$Date.csv"
for MasterClip in `cat $1`
do
echo $MasterClip
String=$(echo "$File" | grep -x $MasterClip)
echo $String
if [ -z $String ];
then
echo "NotFound"
echo $MasterClip >> "$3-NotFound-$Date.csv"
else
echo "Found"
echo $MasterClip >> "$3-Found-$Date.csv"
fi
done
fi
My guess is that the below line of code is not working, as whenever I am running the code, the string value is empty only. It is not catching the search value into it.
String=$(echo "$File" | grep -x $MasterClip)
echo $String
I tried multiple things but not sure where I am going wrong.
THanks for helping me out

Conditional statement bash script

I need help with replacing the following script with a different format where a configuration file, and a loop is used.
[FedoraC]$ cat script.sh
#!/bin/bash
grep -q /tmp /etc/fstab
if [ $? -eq 0 ]; then
echo "True"
else
echo "False"
fi
mount | grep ' /tmp' | grep nodev
if [ $? -eq 0 ]; then
echo "True"
else
echo "False"
fi
mount | grep /tmp | grep nosuid
if [ $? -eq 0 ]; then
echo "True"
else
echo "False"
fi
So far I have the following script which should take the values from a source/conf file and run each command found in the conf file one by one. After the command is executed the output would be "True" or "False"
conf file is formed by Unix commands: /opt/conf1
[FedoraC]$ cat conf1
grep -q /tmp /etc/fstab
mount | grep /tmp | grep nodev
mount | grep /tmp | grep nosuid
mount | grep /tmp | grep noexec
[FedoraC]$ cat new_script.sh
#!/bin/bash
. conf1
for i in $#;
do $i
if [ $i -eq 0 ]; then
echo "Passed"
else
echo "Failed"
fi
done
Instead of displaying the output based on the conditional statement, the script runs each line one by one from conf1, and not echo messages are seen.
Can I get some help please.
try this:
#! bin/bash
while read L; do
echo $L'; exit $?'|sh
if [ $? -eq 0 ]; then
echo Pass
else
echo Failed
fi
done < conf1
The more robust and canonical way to do this would be to have a directory /opt/conf1.d/, and put each of your lines as an executable script in this directory. You can then do
for file in /opt/conf1.d/*
do
[[ -x $file ]] || continue
if "$file"
then
echo "Passed"
else
echo "Failed"
fi
done
This has the advantages of supporting multi-line scripts, or scripts with more complex logic. It also lets you write the check script in any language, and lets scripts and packages add and remove contents easily and non-interactively.
If you really want to stick with your design, you can do it with:
while IFS= read -r line
do
if ( eval "$line" )
then
echo "Passed"
else
echo "Failed"
fi
done < /opt/conf1
The parentheses in the if statement runs eval in a subshell, so that lines can't interfere with each other by setting variables or exiting your entire loop.

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