An array holds the files accessed, and the archive files are split into smaller sizes in preparation for online backup. I am attempting to retrieve the exit code for each iteration through the loop of the split command. However, it is returning Exit Code 1, yet it says that the operation was successful. Why?
#!/bin/bash
declare -a SplitDirs
declare -a CFiles
CDIR=/mnt/Net_Pics/Working/Compressed/
SDIR=/mnt/Net_Pics/Working/Split/
Err=/mnt/Net_Pics/Working
SplitDirs=(`ls -l "$CDIR" --time-style="long-iso" | egrep '^d' | awk '{print $8}'`)
for dir in "${SplitDirs[#]}"
do
if [ ! -d "$SDIR""$dir" ]; then
mkdir "$SDIR""$dir"
else continue
fi
CFiles=(`ls -l "$CDIR$dir" --time-style="long-iso" | awk '{print $8}'`)
for f in "${CFiles[#]}"
do
if [ ! -e "$SDIR""$dir"/"$f" ]; then
split -d -a 4 -b 1992295 "$CDIR""$dir"/"$f" "$SDIR""$dir"/"$f" --verbose
if [[ "$?" == 1 ]]
then
rm -rf "$SDIR""$dir" && echo "$SDIR""$dir" "Removed due to Error code" "$?""." "Testing Archives and Retrying..." 2>&1 | tee "$Err"/Split_Err.log
7z t "$CDIR""$dir"/"$f" >> tee stdout.log 2>> "$Err"/"$dir"/7z_Err.log >&2
mkdir "$SDIR""$dir" && split -d -a 4 -b 1992295 "$CDIR""$dir"/"$f" "$SDIR""$dir"/"$f" --verbose
if [[ "$?" == 1 ]]
then
rm -rf "$SDIR""$dir" && echo "$SDIR""$dir" "Removed a second time due to Error code "$?". Skipping..." 2>&1 | tee "$Err"/Split_Err.log
continue
else
echo "Split Success:" "$SDIR""$dir"/"$f" "ended with Exit status" "$?" && continue
fi
else
echo "Split Success:" "$SDIR""$dir" "ended with Exit status" "$?" && continue
fi
else
echo "$SDIR""$dir"/"$f" "Exists... Skipping Operation" 2>&1 | tee "$Err"/"$dir"/Split_Err.log
continue
fi
done
(The echo piping in a previous revision of the question was misplaced code, and thank you for pointing that out. The exit code remains the same, though. Overall,the script does what I want it to except for the exit code portion.)
Remove | echo $?. you are processing the return code of echo command(last command).
Related
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 have tried a method using timeout to identify and exit server comes to RUNNING state or else exit watching for RUNNING state at the timeout value, unfortunately one of the server has bash version 3 and doesn't support time out.
timeout 300s grep -q 'Server state changed to RUNNING' <(tail -f AdminServer.out)
if [ $? != 0];then
printf "\n===> unable to bring up the server.please check\n"
else
printf "\n===> server came to RUNNING state\n"
fi
I have tried the following but wasn't able to exit when the log is appeared
( cmdpid=$BASHPID; (sleep 60; kill $cmdpid) & exec `grep -q 'Server state changed to RUNNING' <(tail -f AdminServer.out)`)
but it first give command line substitution error for grep -q 'Server state changed to RUNNING' <(tail -f AdminServer.out) and then gives error for kill usage like below(seems the kill is not working) ,
kill: usage: kill [-s sigspec | etc....
I'm not in a position to upgrade the bash version of the server unfortunately, any guidance for this highly appreciated .Thank you!!
*server: suse distribution 2.6.32.12-0.7-default
Consider to try something like this:
data=$(
tail -f AdminServer.out & pid=$!
till=$[SECONDS+300]
while ((SECONDS<till)); do :; done
kill -9 $pid
)
grep -q 'Server state changed to RUNNING' <<< "$data" \
&& printf "\n===> server came to RUNNING state\n" \
|| printf "\n===> unable to bring up the server.please check\n"
When I was young... on OSF1, DEC Alpha, SPARC...
without timeout command, without <(...) or <<< operators...
I'm actively waiting...
File: timeout.sh
#! /bin/bash
PID="$1"
SEC="$2"
FILE="$3"
DATE_REF=$(date +%s)
DATE_MAX=$(( DATE_REF + ${SEC} ))
while ps -p "${PID}" >/dev/null 2>&1 && [[ $(date +%s) -lt ${DATE_MAX} ]] && ! grep OK "${FILE}" >/dev/null 2>&1; do
sleep 1
done
if grep OK "${FILE}" >/dev/null 2>&1; then
RET=0
else
RET=1
fi
if ps -p "${PID}" >/dev/null 2>&1; then
kill -9 "${PID}"
fi
rm -f "${FILE}"
exit $RET
Used like that:
nb_seconds_to_wait=5
tail -f my_log_file | grep -m 1 my_token_to_search && echo "OK" > my_flag_file &
timeout.sh $! nb_seconds_to_wait my_flag_file
if [[ $? -eq 0 ]]; then
echo "Found"
else
echo "Not found"
fi
I have tried again and came up with the following, am I doing anything harmful here?
( cmdpid=$BASHPID; ( sleep 10; if ps -p $cmdpid > /dev/null; then kill $cmdpid > /dev/null;fi ) & while `tail -f -n0 AdminServer.out | grep -q RUNNING`; do exit 0;done )
echo "proceeding with the rest of commands the previous command status is $?"
This is the code for wal archive clean up in postgresql. I am passing archive path and age before when wal needs to be cleaned up. Its a wrapper scrip but somehow its not working. Whenever I pass arguments it throws first message for passing correct parameters even though I am giving correct one.
#!/bin/bash
if [ "$#" -ne 2 ]; then
echo -e "This script helps in cleaning up archived log files in postgres. Give the command with parameters in order\t
sh walarchivecleanup.sh -p archivepath -a age (days) "
echo "Usage : walarchivecleanup.sh -p archivepath -a age"
echo -e "\t -p <value> -- Path to the archived WAL logs (e.g. /pg_data/pg_xlog/archive)"
echo -e "\t -a <value> -- Age of archived logs to keep (days), anything older will be deleted"
exit 1
else
echo -e "Do Nothing"
fi
archivepath=$1
age=$2
##########################################################
while getopts "p:a" opt;
do
case ${opt} in
p) archivepath=${OPTARG};;
a) age=${OPTARG};;
\? )
echo "Usage: sh walarchivecleanup.sh -p archivepath -a age (days) "
;;
esac
done
###############################################################
if [[ -z $archivepath ]]; then echo "Error: Missing archivepath"; exit 1; fi
if [[ -z $age ]]; then echo "Error: Age (-a) must be given"; exit 1; fi
if ! [[ -d $archivepath ]]; then
echo "Error: archivepath not found"; exit 1
else
cmd_path=$archivepath
fi
if [[ -n $archivecleanup ]]; then
if ! [[ -x $archivecleanup ]]; then
echo "Error: Command $archivecleanup not found or no permission to execute"; exit 1;
else
cmd_command="$archivecleanup"
fi
else
if ! `which pg_archivecleanup 1>/dev/null`; then echo "Error: Command pg_archivecleanup not found"; exit 1; fi
cmd_command="pg_archivecleanup"
fi
if [[ -n $age ]]; then
cmd_file="$(find ${archivepath}/ -type f -mtime +${age} -printf "%C# %f\n" |sort -n | tail -n 1 | awk '{print $NF}')"
else
cmd_file="$archivefile"
fi
execute="$cmd_command $cmd_path $cmd_file"
`$execute`
exit $?
echo "Unknown Error - Should never reach this part"
exit 1
I am trying to add a second pre-commit script and it seems not to be catching when I place it in the hook.
The first script basically locks a file from being editing. The second script look at a path and compares a string value to a file that is being committed and if it matches then it will error.
#!/bin/sh
REPOS="$1"
TXN="$2"
GREP=/bin/grep
SED=/bin/sed
AWK=/usr/bin/awk
SVNLOOK=/usr/bin/svnlook
AUTHOR=`$SVNLOOK author -t "$TXN" "$REPOS"`
if [ "$AUTHOR" == "testuser" ]; then
exit 0
fi
if [ "$AUTHOR" == "" ]; then
exit 0
fi
CHANGED=`$SVNLOOK changed -t "$TXN" "$REPOS" | $GREP "^[U|A]" | $AWK '{print $2}'`
COMPARE=`$SVNLOOK diff -t "$TXN" "$REPOS"`
#Operation 001 Beginning
#Restrict users from commiting against testfile
for PATH in $CHANGED
do
if [[ "$PATH" == *path/to/file/testfile.txt ]]; then
#allow testuser to have universal commit permissions in this path.
if [ "$AUTHOR" == "testuser" ]; then
exit 0
else
#User is trying to modify testfile.txt
echo "Only testuser can edit testfile.txt." 1>&2
exit 1
fi
fi
done
#Operation 001 Completed
#Operation 002 Beginning
#Restrict commits based on string found in file
for PATH in $COMPARE
do
if [[ "$PATH" == *path/to/look/at/only/* ]]; then
$SVNLOOK diff -t "$TXN" "$REPOS" | egrep 'string1|string2|string3' > /dev/null && { echo "Cannot commit using string1, string2 or string3 in files trying to commit" 1>&2; exit 1; }
else exit 0;
fi
done
#Operation 002 Completed
It keeps successfully committing the file even though the string is present. Any ideas why it wouldn't be catching it?
Your first test:
if [ "$AUTHOR" == "testuser" ]; then
exit 0
fi
It causes an abort (with zero exit value) if the AUTHOR is testuser!
So your second test:
if [ "$AUTHOR" == "testuser" ]; then
exit 0
else
#User is trying to modify testfile.txt
echo "Only testuser can edit testfile.txt." 1>&2
exit 1
fi
It's unnecessary because at this point the AUTHOR isn't testuser!
And maybe would better instead of your for-loop:
if $SVNLOOK changed -t "$TXN" "$REPOS" | $GREP "^[U|A]" | $AWK '{print $2}' | grep -q 'path/to/file/testfile.txt'; then
echo "Only testuser can edit testfile.txt." 1>&2
exit 1
fi
The if [[ "$PATH" == *path/to/file/testfile.txt ]]; then test doesn't work because this test doesn't understand shell variables (and would better enclose between quotation marks because of *).
And I would replace the
for PATH in $COMPARE
do
if [[ "$PATH" == *path/to/look/at/only/* ]]; then
part to
if echo ${COMPARE} | grep -q "path/to/look/at/only"; then
I am trying to have a script take an argument and run a command against it and retry the command if a result matches a certain string. Otherwise other results will echo the result.
We have a script at work where we run "rm <filename>" but it takes 5 seconds to return whether successful or not and I want to make a script to keep running it until it sees a "destroyed file" string in the result.
This is what I have so far..
#!/bin/bash
function rm () {
if [[ $(rm $1 | grep -m 1 'destroyed file') ]] ; then
echo "Destroyed File"
elif [[ $(rm $1 | grep -m 1 'remove failed') ]] ; then
sleep 5;
rm $1
elif [[ $(rm $1 | grep -m 1 'not found') ]] ; then
echo "file not found"
elif [[ $(rm $1 | grep -m 1 'You must have root access') ]] ; then
echo "You do not have root access"
else
echo "Incorrect parameter or command"
fi
}