Use of shell variable inside command - linux

my server is using CentOS 5.5 (which is almost Red Hat Linux).
I want to backup a set of pictures into time-stamped files.
This code would work:
z_cmd1=$(tar cvzf /home/user1/public_ftp/misc/pics_20100925_142230.tar.gz /home/user1/public_html/misc/_pics_var/F???????.jpg)
echo "tar output =[${z_cmd1}]"
but of course I want the time stamp to be automatic.
The following code does not work. Somehow, the third line (the one with the tar) fails. 'tar' does something, but it does not create any file at the expected destination folder. Why?
z_fname=$(date +"/home/user1/public_ftp/misc/pics_%Y%m%d_%H%M%S.tar.gz")
echo "File name =[${z_fname}]"
z_cmd1=$(tar cvzf ${z_fname} /home/user1/public_html/misc/_pics_var/F???????.jpg)
echo "tar output =[${z_cmd1}]"
Thank you.

Try this something like this:
mydate=`date +"%m-%d-%Y"`;
input="/home/user1/public_html/misc/_pics_var/F???????.jpg";
output="/home/user1/public_ftp/misc/pics_$mydate.tar.gz";
tar cvzf $output $input && echo "$output created succesfully!";
if [ ! $? == 0 ]; then echo "$output failed"; fi;
We create a date as variable.
We create the input and output variables, using the date variable in the output variable.
Then execute the command and check if it executed without errors.

Related

How do I execute .AppImage files using regular expressions in bash scripts?

I want to execute a .AppImage program using regular expressions because the .AppImage updates it's name every time I update it.
My code:
#!/bin/bash
./home/myname/Applications/myApplication-*.AppImage
Output:
./launch_myApplication.sh: line 3: ./home/myname/Applications/myApplication-*.AppImage: No such file or directory
The real .AppImage name is "myApplication-2-11-2.AppImage", but as written above: the verison number changes every update.
How do I execute .AppImage files using regular expressions in bash scripts?
run in relative path
cd YOUR-TARGET-DIRECTORY
# save the name in cmd
cmd=$(echo myApplication-*.AppImage)
# run it
./$cmd
run in absolute path
cmd=$(echo /home/myname/Applications/myApplication-*.AppImage)
# WRONG
#./$cmd
# CORRECT
$cmd
safer
#!/bin/bash
filename='/home/myname/Applications/myApplication-*.AppImage';
if [[ -f "$filename" ]]; then
$filename;
else
echo "$filename not found";
fi
NOTE
if your $HOME is /home/myname you need to update your script
#!/bin/bash
# wrong
# ./home/myname/Applications/myApplication-*.AppImage
# right
/home/myname/Applications/myApplication-*.AppImage

BASH save stdout to new file upon execution

please bear with me if my terminology or syntax is less than stellar (still learning). I currently have a simple bash script that checks the arguments of the command and outputs files names with matching text. This part of my script works correctly via a grep command and piped to xargs for proper formatting.
When running the script, I run through a simple loop to check if the value is null and then move to running my variable/search if not.
My question is: Is it possible to have this script output via stdout AND also save a new file each time it is run with the user input and date/time? (but not overwrite) EX: report-bob-0729161500.rpt
I saw same other suggestions to use tee with the command, but I was trying to get it to work within the script. Similarly, another suggestion stated to utilize exec > >(tee -i logfile.txt), but I am unsure how to properly format this to include the date/time and $1 input into new files each time the script is executed.
Any help or suggested resources?
Thank you.
SEARCH=`[search_variable]`
if [ -z "$SEARCH" ]
then
echo "$1 not found."
else
echo -e "REPORT LISTING\n\n"
echo "$SEARCH"
fi
EDIT: I did try simply piping the echo statements to the tee command, which does work. However, I am still curious if anyone has other suggestions to accomplish this same task via alternative methods. Thank you.
With echo statements piped to tee:
SEARCH=`[search_variable]`
DATE=`date +"%m%d%y%k%M"`
if [ -z "$SEARCH" ]
then
echo "$1 not found."
else
echo -e "REPORT LISTING\n\n" | tee tps-list-$1-$DATE.rpt
echo "$SEARCH" | tee tps-list-$1-$DATE.rpt
fi
If you want to do it within the script, why then not just write to
both standard output and the file (using append where appropriate?).
Maybe a bit more writing, but it gives complete control.
Leon

bash - wget -N if else value check

I'm working on a bash script that pulls a file from an FTP site only if the timestamp on remote is different than local. After it puts the file, it copies the file over to 3 other computers via samba (smbclient).
Everything works, but the file copies even if the wget -N ftp://insertsitehere.com returns a value that the file on the remote was not newer. What would be the best way to check the output of the script so that the copy only happens if a new version was pulled from FTP?
Ideally, I'd like the copy to the computers to preserve the timestamp just like the wget -N command does, too.
Here is an example of what I have:
#!/bin/bash
OUTDIR=/cats/dogs
cd $OUTDIR
wget -N ftp://user:password#sitegoeshere.com/filename
if [ $? -eq 0 ]; then
HOSTS="server1 server2 server3"
for i in $HOSTS; do
echo "Uploading to $i..."
smbclient -A /root/.smbclient.authfile //$i/path -c "lcd /cats/dogs; put fiilename.txt"
if [ $? -eq 0 ]; then
echo "Upload to $i successful..."
else
echo "There was an issue uploading to host $i..."
fi
done
else
echo "There was an issue with the FTP Download...."
exit 1
fi
The return value of wget is different than 0 only if there is an error. If -N is in use and the remote file is older than the local file, it will still have a return value of 0, so you cannot use that to check if the file has been modified.
You could check the mtime of the file to see if it changed, or the content. For example, you could use something like:
md5_old=$( md5sum filename.txt 2>/dev/null )
wget -N ftp://user:password#sitegoeshere.com/filename.txt
md5_new=$( md5sum filename.txt )
if [ "$md5_old" != "$md5_new" ]; then
# Copy filename.txt to SMB servers
fi
Regarding smbclient, unfortunately there is no way to preserve timestamps in either get or put commands. If you need it, you must use some different tool (scp -p, rsync -t...)
touch -r foo.txt foo.old
wget -N example.com/foo.txt
if [ foo.txt -nt foo.old ]
then
echo 'Uploading to server1...'
fi
"Save" the current timestamp into a new empty file
Use wget --timestamping to only download the file if it is newer
If file is newer than the "save" file, do stuff

launch process in background and modify it from bash script

I'm creating a bash script that will run a process in the background, which creates a socket file. The socket file then needs to be chmod'd. The problem I'm having is that the socket file isn't being created before trying to chmod the file.
Example source:
#!/bin/bash
# first create folder that will hold socket file
mkdir /tmp/myproc
# now run process in background that generates the socket file
node ../main.js &
# finally chmod the thing
chmod /tmp/myproc/*.sock
How do I delay the execution of the chmod until after the socket file has been created?
The easiest way I know to do this is to busywait for the file to appear. Conveniently, ls returns non-zero when the file it is asked to list doesn't exist; so just loop on ls until it returns 0, and when it does you know you have at least one *.sock file to chmod.
#!/bin/sh
echo -n "Waiting for socket to open.."
( while [ ! $(ls /tmp/myproc/*.sock) ]; do
echo -n "."
sleep 2
done ) 2> /dev/null
echo ". Found"
If this is something you need to do more than once wrap it in a function, but otherwise as is should do what you need.
EDIT:
As pointed out in the comments, using ls like this is inferior to -e in the test, so the rewritten script below is to be preferred. (I have also corrected the shell invocation, as -n is not supported on all platforms in sh emulation mode.)
#!/bin/bash
echo -n "Waiting for socket to open.."
while [ ! -e /tmp/myproc/*.sock ]; do
echo -n "."
sleep 2
done
echo ". Found"
Test to see if the file exists before proceeding:
while [[ ! -e filename ]]
do
sleep 1
done
If you set your umask (try umask 0) you may not have to chmod at all. If you still don't get the right permissions check if node has options to change that.

Equivalent of %~dp0 (retrieving source file name) in sh

I'm converting some Windows batch files to Unix scripts using sh. I have problems because some behavior is dependent on the %~dp0 macro available in batch files.
Is there any sh equivalent to this? Any way to obtain the directory where the executing script lives?
The problem (for you) with $0 is that it is set to whatever command line was use to invoke the script, not the location of the script itself. This can make it difficult to get the full path of the directory containing the script which is what you get from %~dp0 in a Windows batch file.
For example, consider the following script, dollar.sh:
#!/bin/bash
echo $0
If you'd run it you'll get the following output:
# ./dollar.sh
./dollar.sh
# /tmp/dollar.sh
/tmp/dollar.sh
So to get the fully qualified directory name of a script I do the following:
cd `dirname $0`
SCRIPTDIR=`pwd`
cd -
This works as follows:
cd to the directory of the script, using either the relative or absolute path from the command line.
Gets the absolute path of this directory and stores it in SCRIPTDIR.
Goes back to the previous working directory using "cd -".
Yes, you can! It's in the arguments. :)
look at
${0}
combining that with
{$var%Pattern}
Remove from $var the shortest part of $Pattern that matches the back end of $var.
what you want is just
${0%/*}
I recommend the Advanced Bash Scripting Guide
(that is also where the above information is from).
Especiall the part on Converting DOS Batch Files to Shell Scripts
might be useful for you. :)
If I have misunderstood you, you may have to combine that with the output of "pwd". Since it only contains the path the script was called with!
Try the following script:
#!/bin/bash
called_path=${0%/*}
stripped=${called_path#[^/]*}
real_path=`pwd`$stripped
echo "called path: $called_path"
echo "stripped: $stripped"
echo "pwd: `pwd`"
echo "real path: $real_path
This needs some work though.
I recommend using Dave Webb's approach unless that is impossible.
In bash under linux you can get the full path to the command with:
readlink /proc/$$/fd/255
and to get the directory:
dir=$(dirname $(readlink /proc/$$/fd/255))
It's ugly, but I have yet to find another way.
I was trying to find the path for a script that was being sourced from another script. And that was my problem, when sourcing the text just gets copied into the calling script, so $0 always returns information about the calling script.
I found a workaround, that only works in bash, $BASH_SOURCE always has the info about the script in which it is referred to. Even if the script is sourced it is correctly resolved to the original (sourced) script.
The correct answer is this one:
How do I determine the location of my script? I want to read some config files from the same place.
It is important to realize that in the general case, this problem has no solution. Any approach you might have heard of, and any approach that will be detailed below, has flaws and will only work in specific cases. First and foremost, try to avoid the problem entirely by not depending on the location of your script!
Before we dive into solutions, let's clear up some misunderstandings. It is important to understand that:
Your script does not actually have a location! Wherever the bytes end up coming from, there is no "one canonical path" for it. Never.
$0 is NOT the answer to your problem. If you think it is, you can either stop reading and write more bugs, or you can accept this and read on.
...
Try this:
${0%/*}
This should work for bash shell:
dir=$(dirname $(readlink -m $BASH_SOURCE))
Test script:
#!/bin/bash
echo $(dirname $(readlink -m $BASH_SOURCE))
Run test:
$ ./somedir/test.sh
/tmp/somedir
$ source ./somedir/test.sh
/tmp/somedir
$ bash ./somedir/test.sh
/tmp/somedir
$ . ./somedir/test.sh
/tmp/somedir
This is a script can get the shell file real path when executed or sourced.
Tested in bash, zsh, ksh, dash.
BTW: you shall clean the verbose code by yourself.
#!/usr/bin/env bash
echo "---------------- GET SELF PATH ----------------"
echo "NOW \$(pwd) >>> $(pwd)"
ORIGINAL_PWD_GETSELFPATHVAR=$(pwd)
echo "NOW \$0 >>> $0"
echo "NOW \$_ >>> $_"
echo "NOW \${0##*/} >>> ${0##*/}"
if test -n "$BASH"; then
echo "RUNNING IN BASH..."
SH_FILE_RUN_PATH_GETSELFPATHVAR=${BASH_SOURCE[0]}
elif test -n "$ZSH_NAME"; then
echo "RUNNING IN ZSH..."
SH_FILE_RUN_PATH_GETSELFPATHVAR=${(%):-%x}
elif test -n "$KSH_VERSION"; then
echo "RUNNING IN KSH..."
SH_FILE_RUN_PATH_GETSELFPATHVAR=${.sh.file}
else
echo "RUNNING IN DASH OR OTHERS ELSE..."
SH_FILE_RUN_PATH_GETSELFPATHVAR=$(lsof -p $$ -Fn0 | tr -d '\0' | grep "${0##*/}" | tail -1 | sed 's/^[^\/]*//g')
fi
echo "EXECUTING FILE PATH: $SH_FILE_RUN_PATH_GETSELFPATHVAR"
cd "$(dirname "$SH_FILE_RUN_PATH_GETSELFPATHVAR")" || return 1
SH_FILE_RUN_BASENAME_GETSELFPATHVAR=$(basename "$SH_FILE_RUN_PATH_GETSELFPATHVAR")
# Iterate down a (possible) chain of symlinks as lsof of macOS doesn't have -f option.
while [ -L "$SH_FILE_RUN_BASENAME_GETSELFPATHVAR" ]; do
SH_FILE_REAL_PATH_GETSELFPATHVAR=$(readlink "$SH_FILE_RUN_BASENAME_GETSELFPATHVAR")
cd "$(dirname "$SH_FILE_REAL_PATH_GETSELFPATHVAR")" || return 1
SH_FILE_RUN_BASENAME_GETSELFPATHVAR=$(basename "$SH_FILE_REAL_PATH_GETSELFPATHVAR")
done
# Compute the canonicalized name by finding the physical path
# for the directory we're in and appending the target file.
SH_SELF_PATH_DIR_RESULT=$(pwd -P)
SH_FILE_REAL_PATH_GETSELFPATHVAR=$SH_SELF_PATH_DIR_RESULT/$SH_FILE_RUN_BASENAME_GETSELFPATHVAR
echo "EXECUTING REAL PATH: $SH_FILE_REAL_PATH_GETSELFPATHVAR"
echo "EXECUTING FILE DIR: $SH_SELF_PATH_DIR_RESULT"
cd "$ORIGINAL_PWD_GETSELFPATHVAR" || return 1
unset ORIGINAL_PWD_GETSELFPATHVAR
unset SH_FILE_RUN_PATH_GETSELFPATHVAR
unset SH_FILE_RUN_BASENAME_GETSELFPATHVAR
unset SH_FILE_REAL_PATH_GETSELFPATHVAR
echo "---------------- GET SELF PATH ----------------"
# USE $SH_SELF_PATH_DIR_RESULT BEBLOW
I have tried $0 before, namely:
dirname $0
and it just returns "." even when the script is being sourced by another script:
. ../somedir/somescript.sh

Resources