How to get logs of individual argument passed to shell script from a file - linux

I have a shell script. In this script I am reading table names for a file and executing a command.
The script is working fine. I am able execute the command for all the tables in the file.
shell script
#!/bin/bash
[ $# -ne 1 ] && { echo "Usage : $0 input file "; exit 1; }
args_file=$1
TIMESTAMP=`date "+%Y-%m-%d"`
touch /home/$USER/logs/${TIMESTAMP}.success_log
touch /home/$USER/logs/${TIMESTAMP}.fail_log
success_logs=/home/$USER/logs/${TIMESTAMP}.success_log
failed_logs=/home/$USER/logs/${TIMESTAMP}.fail_log
#Function to get the status of the job creation
function log_status
{
status=$1
message=$2
if [ "$status" -ne 0 ]; then
echo "`date +\"%Y-%m-%d %H:%M:%S\"` [ERROR] $message [Status] $status : failed" | tee -a "${failed_logs}"
#echo "Please find the attached log file for more details"
#exit 1
else
echo "`date +\"%Y-%m-%d %H:%M:%S\"` [INFO] $message [Status] $status : success" | tee -a "${success_logs}"
fi
}
while read table ;do
spark-submit hive.py $table
done < ${args_file}
g_STATUS=$?
log_status $g_STATUS "Spark ${table}"
In this script I want to collect status logs and stdout logs. I want to collect the logs for each table in the file individually.
I want to know if the execution of spark-submit has been successful or failed for each table in the file. Say the status logs
How can I collect stdout files for each table individually and store them at a location in Linux.
What are the changes I need to do to achieve my results.

Make sure just to re-direct (stdout) of the logs generated for each of the table instance in your script to a folder under /var/log/ may be call it as myScriptLogs
mkdir -p /var/log/myScriptLogs || { echo "mkdir failed"; exit; }
while read -r table ;do
spark-submit hive.py "$table" > /var/log/myScriptLogs/"${table}_dump.log" 2>&1
done < "${args_file}"
The script will fail if you are not able to create a new directory using mkdir for some reason. So this creates a log for each table being processed under /var/log as <table_name>_dump.log which you can change it to however way you want.
Couple of best practices would be to use -r flag in read and double-quote shell variables.
Answer updated to redirect stderr also to the log file.

Related

Shell script to automate sftp transfer

I'm trying to create a shell script to automate the sftp transfer from source server to remote server without prompting for password. But when I run the script I get the output that no file is found for transfer (I have created files in the source directory). Here is my code
tempfile="/tmp/sftpsync.$$"
count=0
trap "/bin/rm -f $tempfile" 0 1 15
if [ $# -eq 0 ] ; then
echo "Usage: $0 user host path_to_src_dir remote_dir" >&2
exit 1
fi
# Collect User Input
user="$1"
server="$2"
remote_dir="$4"
source_dir="$3"
timestamp="$source_dir/.timestamp"
# Without source and remote dir, the script cannot be executed
if [[ -z $remote_dir ]] || [[ -z $source_dir ]]; then
echo "Provide source and remote directory both"
exit 1
fi
echo "cd $remote_dir" >> $tempfile
# timestamp file will not be available when executed for the very first time
if [ ! -f $timestamp ] ; then
# no timestamp file, upload all files
for filename in $source_dir/*
do
if [ -f "$filename" ] ; then
# Place the command to upload files in sftp batch file
echo "put -P \"$filename\"" >> $tempfile
# Increase the count value for every file found
count=$(( $count + 1 ))
fi
done
else
# If timestamp file found then it means it is not the first execution so look out for newer files only
# Check for newer files based on the timestamp
for filename in $(find $source_dir -newer $timestamp -type f -print)
do
# If found newer files place the command to upload these files in batch file
echo "put -P \"$filename\"" >> $tempfile
# Increase the count based on the new files
count=$(( $count + 1 ))
done
fi
# If no new files found the do nothing
if [ $count -eq 0 ] ; then
echo "$0: No files require uploading to $server" >&2
exit 1
fi
# Place the command to exit the sftp connection in batch file
echo "quit" >> $tempfile
echo "Synchronizing: Found $count files in local folder to upload."
# Main command to use batch file with SFTP shell script without prompting password
sftp -b $tempfile "$user#$server"
echo "Done. All files synchronized up with $server"
# Create timestamp file once first set of files are uploaded
touch $timestamp
# Remove the sftp batch file
rm -f $tempfile
exit 0
But I always get the output as "No files require uploading to $server". Would like to check if there is any issue with my code or how can this be fixed?

Use of echo >> produces inconsistent results

I've been trying to understand a problem that's cropped up with some of the scripts we use at work.
To generate many of our script logs, we utilize the exec command and file redirects to print all output from the script to both the terminal and a log file. Occasionally, for information that doesn't need to be displayed to the user, we do a straight redirect to the log file.
The issue we're seeing occurs on the last line of output to the file when we're printing the number of errors that occurred during that execution: The text doesn't get printed to the file.
In an attempt to diagnose the problem, I wrote a simplified version of our production script (script1.bash) and a test script (script2.bash) to try to tease out the problem.
script1.bash
#!/bin/bash
log_name="${USER}_`date +"%Y%m%d-%H%M%S"`_${HOST}_${1}.log"
log="/tmp/${log_name}"
log_tmp="/tmp/temp_logs"
err_count=0
finish()
{
local ecode=0
if [ $# -eq 1 ]; then
ecode=${1}
fi
# This is the problem line
echo "Error Count: ${err_count}" >> "${log}"
mvlog
local success=$?
exec 1>&3 2>&4
if [ ${success} -ne 0 ]; then
echo ""
echo "WARNING: Failed to save log file to ${log_tmp}"
echo ""
ecode=$((ecode+1))
fi
exit ${ecode}
}
mvlog()
{
local ecode=1
if [ ! -d "${log_tmp}" ]; then
mkdir -p "${log_tmp}"
chmod 775 "${log_tmp}"
fi
if [ -d "${log_tmp}" ]; then
rsync -pt --bwlimit=4096 "${log}" "${log_tmp}/${log_name}" 2> /dev/null
[ $? -eq 0 ] && ecode=0
if [ ${ecode} -eq 0 ]; then
rm -f "${log}"
fi
fi
}
exec 3>&1 4>&2 >(tee "${log}") 2>&1
ecode=0
echo
echo "Some text"
echo
finish ${ecode}
script2.bash
#!/bin/bash
runs=10000
logdir="/tmp/temp_logs"
if [ -d "${logdir}" ]; then
rm -rf "${logdir}"
fi
for i in $(seq 1 ${runs}); do
echo "Conducting run #${i}/${runs}..."
${HOME}/bin/script1.bash ${i}
done
echo "Scanning logs from runs..."
total_count=`find "${logdir}" -type f -name "*.log*" | wc -l`
missing_count=`grep -L 'Error Count:' ${logdir}/*.log* | grep -c /`
echo "Number of runs performed: ${runs}"
echo "Number of log files generated: ${total_count}"
echo "Number of log files missing text: ${missing_count}"
My first test indicated roughly 1% of the time the line isn't written to the log file. I then proceeded to try several different methods of handling this line of output.
Echo and Wait
echo "Error Count: ${err_count}" >> "${log}"
wait
Alternate print method
printf "Error Count: %d\n" ${err_count} >> "${log}"
No Explicit File Redirection
echo "Error Count: ${err_count}"
Echo and Sleep
echo "Error Count: ${err_count}" >> "${log}"
sleep 0.2
Of these, #1 and #2 each had a 1% fail rate while #4 had a staggering 99% fail rate. #3 was the only methodology that had a 0% fail rate.
At this point, I'm at a loss for why this behavior is occurring, so I'm asking the gurus here for any insight.
(Note that the simple solution is to implement #3, but I want to know why this is happening.)
Without testing, this looks like a race condition between your script and tee. It's generally better to avoid multiple programs writing to the same file at the same time.
If you do insist on having multiple writers, make sure they are all in append mode, in this case by using tee -a. Appends to the local filesystem are atomic, so all writes should make it (this is not necessarily true for networked file systems).

output of shell script to console and file

I have shell script in Linux like below
#!/bin/bash
LOG_LOCATION=/home/$USER/logs
exec > >(tee /home/$USER/logs/"$1") 2>&1
[ $# -ne 1 ] && { echo "Usage : $0 table ";exit 1; }
table=$1
TIMESTAMP=`date "+%Y-%m-%d"`
touch /home/$USER/logs/${TIMESTAMP}.success_log
touch /home/$USER/logs/${TIMESTAMP}.fail_log
success_logs=/home/$USER/logs/${TIMESTAMP}.success_log
failed_logs=/home/$USER/logs/${TIMESTAMP}.fail_log
#Function to get the status of the job creation
function log_status
{
status=$1
message=$2
if [ "$status" -ne 0 ]; then
echo "`date +\"%Y-%m-%d %H:%M:%S\"` [ERROR] $message [Status] $status : failed" | tee -a "${failed_logs}"
#echo "Please find the attached log file for more details"
exit 1
else
echo "`date +\"%Y-%m-%d %H:%M:%S\"` [INFO] $message [Status] $status : success" | tee -a "${success_logs}"
fi
}
`hive -e "create table testing.${table} as select * from fishing.${table}"`
cp /home/$USER/logs/"$1" /home/$USER/debug/"$1"
g_STATUS=$?
log_status $g_STATUS "Hive create ${table}"
echo "***********************************************************************************************************************************************************************"
If I have this in my shell script
exec 2>&1 | tee /home/logging/"$1"
Then I am getting logs only on console not on the redirected file.
If I have this in my script
exec> /home/logging/"$1" 2>&1
Then I am having logs on the redirected file but not on the console.
How can I have logs both on console and redirected file
You can use process substitution with exec builtin:
exec > >(tee trace.log) 2>&1
to redirect both stdout and stderr to a file as as well as show it in terminal.
The purpose of the tee command is specifically intended to direct the output to both a file and to the terminal, which is what it sounds like you're wanting. This can be replicated pretty easily with something like the following:
script.sh:
#!/usr/bin/bash
date 2>&1 | tee "$1"
Then, running the command with ./script.sh abc.txt will produce the output of the date command to the terminal as well as the file abc.txt
In your case, exec 2>&1 | tee /home/logging/"$1" should correctly produce the results you want, but you will need to call the script with that argument carefully. That assumes the /home/logging directory exists, and you call the script above with something like ./script log.txt

Shell script to check if the process is already running and exit if yes

I have a shell script with methods status() and start(). The code is below:
#function to check the jmeter processes running
status(){
PID=$(ps -ef | grep jmeter|grep -v grep)
echo "The jmeter processes running are: \n$PID"
}
#function to run the .jmx file given by the user at run time
start(){
echo "Please enter the file name .jmx extension"
read file
echo "Please enter the log file name .jtl extension"
read log_file
sh /home/ubuntu/apache-jmeter-3.0/bin/jmeter.sh -n -t $file -l $log_file &
}
while [ "$1" != "" ]; do
case "$1" in
start)
jmeter_start
;;
status)
jmeter_status
;;
*)
echo $"Usage: $0 {start|status}"
exit 1
esac
shift
done
now when I run this script, I have to check if it is already running and if it is running I have to exit. Let me know how to do this.
Add a flag at the beginning of the function and set it to 1, before the end of the function set it to 0, query it however you like.
#function to check the jmeter processes running
status(){
PID=$(ps -ef | grep jmeter|grep -v grep)
echo "The jmeter processes running are: \n$PID"
}
#function to run the .jmx file given by the user at run time
start(){
export start_flag=1
echo "Please enter the file name .jmx extension"
read file
echo "Please enter the log file name .jtl extension"
read log_file
sh /home/ubuntu/apache-jmeter-3.0/bin/jmeter.sh -n -t $file -l $log_file &
export start_flag=0
}
Another option would be writing to an external file and query it.
You actually have most of it already. You should be able to use the code from status that gets the PID and just check if it exists. If it does, output some error and exit. Otherwise, do what you already have.
start(){
PID=$(ps -ef | grep jmeter|grep -v grep)
if [ -z $PID ]; then
echo "Error: JMeter already running"
exit
fi
echo "Please enter the file name .jmx extension"
read file
echo "Please enter the log file name .jtl extension"
read log_file
sh /home/ubuntu/apache-jmeter-3.0/bin/jmeter.sh -n -t $file -l $log_file &
}

after truncated log file in linux,the new created file was filled with many \0

Firstly,i will give the shell code:
#!/bin/bash
filename=$1
if [ -e $filename ] ; then
yesterday=`date -d yesterday +%Y%m%d`
cp $filename $filename.$yesterday
now=`date '+%Y-%m-%d%H:%M:%S'`
echo "========split log at $now========" > $filename
echo "========split log $filename to $filename.$yesterday at $now========"
else
echo "$filename not exist."
fi
The shell run successfully,and print the string ========split log at $now======== to the new created $filename.But below this string,many bytes of \0 are also written to the$filename,which is showed as follows:
My reputation score is less than 10,i can not post image,so i give the link of the picture:http://i.stack.imgur.com/QF0F2.jpg
i wrote the shell code aimed to truncate the log file created by nohup.
The original of my start command like this : nohup $cmd > $logPath 2>&1 &,
now i change it to nohup $cmd >> $logPath 2>&1 &.Someone told me that when use the mode of > the log writer program would remember the location of current log, and after truncating the log,the program will continue the location.

Resources