Tailing log until certain time threshold is reached - linux

I'm tailing a log like this:
while [[ ! -n "${ready}" ]]; do
start_info=`grep "Ready" $LOG_FILE`
sleep 10
done
If the log file doesn't have "Ready" inside this goes on forever, how can I make it run for lets say 200 seconds? Like some kind of time threshold.
Thanks

This avoids repeatedly grepping the whole file:
start=$SECONDS
limit=200
while read -r line
do
if [[ $line =~ $ready ]]
then
start_info=$line
break
fi
if (( $SECONDS >= start + limit ))
then
break
fi
done < "$LOG_FILE"

If I got your question correctly (while [[ ! -n "${ready}" ]]; do is confusing) here is an example how you can check time threshold:
#!/bin/sh
...
timelimit=200
pausetime=10
while [[ -z "${start_info}" ]]; do
start_info=`grep "Ready" $LOG_FILE`
sleep $pausetime
timelimit=$((timelimit - $pausetime))
if [ $timelimit -le 0 ]; then
break
fi
done

Related

How to record each command's duration/result and display this information with `history`?

I have some commands that take a really long time to run. I'd like to be able to keep track of all of them in history.
I've looked into history flags, but those seem to only show the time/date the command started and the status code.
Partially answering your question, here is a function I have in my .zshrc to send a desktop notification when a command took more than 10 s.
You may adapt to build your own history file.
(To have the notifications, you must have notify-send or adapt $notifier)
notifier=notify-send
if [[ ${TERM[1,5]} == xterm ]]; then
preexec() {
notifiable_cmd=$1
notifiable_start_time=`date +%s`
}
precmd() {
if (( $? == 0 )); then
notifiable_status="Success in "
else
notifiable_status="Failed in "
fi
if [[ "$notifiable_cmd" != "" ]]; then
(( notifiable_time = `date +%s` - $notifiable_start_time ))
if [[ $notifiable_time -ge 60 ]]; then
notifiable_str_time="$(($notifiable_time%100000/60)) min $(($notifiable_time%60)) s"
else
notifiable_str_time="$notifiable_time[1,5] s"
fi
if [[ $notifiable_time -gt 10 ]]; then
$notifier $notifiable_cmd "$notifiable_status $notifiable_str_time"
fi
fi
notifiable_cmd=
}
fi
(Adapted from this article Growl upon job completion in zsh on contrapunctus.net)

Linux shutdown with countdown. (made with while)

Hello I want to create a script that shutdowns Linux after a certain time (e.g 15 seconds) with a countdown displayed in the terminal (Linux is shutting down after 15s.). It has to be with while.
You can use the following :
#!/bin/bash
COUNTER=15
while [ 1 ]
do
if [ ${COUNTER} -eq 0 ]
then
break
fi
echo "Linux is shutting down after ${COUNTER}s."
sleep 1
COUNTER=$( echo "${COUNTER}-1" | bc )
done
sudo poweroff
If you really want to display the counter on the screen, this would work:
#!/bin/bash
seconds=15
echo "Linux is shutting down after ${seconds}s"
date_1=$((`date +%s` + $seconds))
while [ "$date_1" -ge `date +%s` ]; do
echo -ne "$(date -u --date #$(($date_1 - `date +%s` )) +%H:%M:%S)\r"
done
echo -e "Do you really want to shutdown the system? Press [Y/y] to shutdown or [n/N] to cancel/exit:"
read var
if [[ "$var" == "y" ]] || [[ "$var" == "Y" ]]
then
sudo poweroff
elif [[ "$var" == "n" ]] || [[ "$var" == "N" ]]
then
exit 1
fi
Basically, it'll accept [Y/y] from the user to shutdown and [n/N] to exit.

Is there a way to automatically restart a bash script? [duplicate]

For example, in the below script startover starts back from the top:
##########################################################################
## CHECK TIME
##########################################################################
time=$(date +%k%M)
if [[ "$time" -ge 1800 ]] && [[ "$time" -le 2200 ]];then
echo "Not a good time to transcode video!" && exit 0
else
echo "Excellent time to transcode video!" && echo "Lets get started!"
fi
##########################################################################
## CHECK TIME
##########################################################################
startover
Also keeping in mind exit 0 should be able to stop the script.
You could "recurse" using the following line:
exec bash "$0" "$#"
Since $0 is the path to the current script, this line starts the script without creating a new process, meaning you don't need to worry about too many restarts overflowing the process table on your machine.
Put it in a while loop. I'd also suggest you add a "sleep" so that you're not racing your machine's CPU as fast as it will go:
while true; do
##########################################################################
## CHECK TIME
##########################################################################
time=$(date +%k%M)
if [[ "$time" -ge 1800 ]] && [[ "$time" -le 2200 ]]; then
echo "Not a good time to transcode video!" && exit 0
else
echo "Excellent time to transcode video!" && echo "Lets get started!"
fi
##########################################################################
## CHECK TIME
##########################################################################
for i in {1..5}; do
echo $i
sleep 1
done
done
DO NOT USE WHILE LOOP at the start of the script since the condition below will exit the script and break the loop.
echo "Not a good time to transcode video!" && exit 0
You can try trapping the exit signal so that when the script exits it restarts
##########################################################################
## CHECK TIME
############bash##############################################################
trap '<path to script> ' EXIT
time=$(date +%k%M)
if [[ "$time" -ge 1800 ]] && [[ "$time" -le 2200 ]];then
echo "Not a good time to transcode video!" && exit 0
sleep 1;
else
echo "Excellent time to transcode video!" && echo "Lets get started!"
sleep 1;
fi
##########################################################################
## CHECK TIME
##########################################################################
echo 1
echo 2
echo 3
echo 4
echo 5
startover
Note: I add a sleep of 1 second because this will give you the time to see message. trap the exit signal and re-running the script is acting like a while loop. I am also assuming that these codes are in a script.
How about enclosing the entire script in a while loop? For example,
while :
do
script
done
You may want to add a condition to break out of the loop.
This is not good practice, but what you asked for.
Put this at the end of your script. "$( cd "$( dirname "$0" )" && pwd )/$(basename $0)"

Bash - Multiple conditions in a for loop

I'm trying to do a for loop with multiple conditions and I didn't find any information about how to do it on the web
I'm sorry for the stupid questions, but I just started programming in linux
what am I doing wrong here?
#!/bin/bash
j=0
for line in `cat .temp_` || j in `seq 0 19`
do
...
done
the error says wrong syntax and that I can't use ||
Thanks a lot
for line in `cat .temp_`
do
if ! grep -Fxq $line $dictionary && [ $j -lt 20 ]
then
echo $line >> $dictionary
j=$((j+1))
fi
[ $j -gt 20 ] && break
done
You can't check a condition in a for loop in shell. You must do it in a extra statement.
In this case so:
[ $j -gt 20 ] && break
Another solution, without break:
while read line && [ $j -lt 20 ]
do
if ! grep -Fxq $line $dictionary && [ $j -lt 20 ]
then
echo $line >> $dictionary
j=$((j+1))
fi
done < .temp_
You are trying to combine two different styles of for-loops into one. Instead, just break if the value of j becomes too large:
j=0
while read -r line; do
[[ j >= 20 ]] && break
...
done < ._temp
(This, by the way, is the preferred way to iterate over a file in bash. Using a for-loop runs into problems if the file is too large, as you essentially building a command line using the entire contents of the file.)
[UPDATE: the following is based on my conjecture as to the purpose of the loop. See Calculate Word occurrences from file in bash for the real context.]
Actually, you can dispense with the loop. You are looking for at most 20 lines from .temp_ that do not already appear in the file whose name is in dictionary, right?
sort -u .temp_ | grep -f $dictionary -Fx -v -m 20 >> $dictionary
This will call grep just once, instead of once per line in .temp_.
A simple nested for-loop?
for line in `cat file`; do for j in {0..19}; do
your_commands
done; done
If I understand you correctly (you want to run a command on every line 20 times).
Once I need to process all files inside a directory, but only until nth file or until all is processed:
count=1
nth=10
for f in folder/*; do
((count++))
# process $f
if [[ $count -gt $nth ]]
then
break
fi
done

Ash MATCH operator (=~)

I'm trying to fit a script for linux onto my WD world edition drive.
The script is written for Bash (debian) but my WD only runs busybox (with ash). Despite this, I have gotten most functionality in there just from using Google. There is only one operator i have not found a counterpart to, the =~ operator
How can i port the functionality of the =~ operator from the old script to ash?
Script:
#! /bin/bash
# posttorrent.sh by Killemov
{
# Log file, file where we tell what events have been processed.
LOG_FILE=/var/log/posttorrent.log
# Username for transmission remote.
TR_USERNAME="username"
# Password for transmission remote.
TR_PASSWORD="password"
# Get current time.
NOW=$(date +%Y-%m-%d\ %H:%M:%S)
# Source directory, should not be changed.
SRC_DIR="${TR_TORRENT_DIR}/${TR_TORRENT_NAME}"
# Directory to store the un-compressed files in..
DEST_DIR="${TR_TORRENT_DIR}/${TR_TORRENT_NAME}/"
# This parameter string could be passed from Transmission in the future.
TR_TORRENT_PARAMETER="EXTRACT SLEEP1h"
echo "text"
if [ -e "$SRC_DIR/keep" ]; then
TR_TORRENT_PARAMETER="$TR_TORRENT_PARAMETER KEEP"
fi
if [ -e "$SRC_DIR/exit" ]; then
TR_TORRENT_PARAMETER="EXIT"
fi
# Actual processing starts here.
if [[ "$TR_TORRENT_PARAMETER" =~ "EXIT" ]]; then
echo $NOW "Exiting $TR_TORRENT_NAME" >> $LOG_FILE
exit 0
fi
echo "text2"
if [[ "$TR_TORRENT_PARAMETER" =~ "EXTRACT" ]]; then
cd $TR_TORRENT_DIR
if [ -d "$SRC_DIR" ]; then
IFS=$'\n'
unset RAR_FILES i
for RAR_FILE in $( find "$SRC_DIR" -iname "*.rar" ); do
if [[ $RAR_FILE =~ .*part.*.rar ]]; then
if [[ $RAR_FILE =~ .*part0*1.rar ]]; then
RAR_FILES[i++]=$RAR_FILE
fi
else
RAR_FILES[i++]=$RAR_FILE
fi
done
unset IFS
if [ ${#RAR_FILES} -gt 0 ]; then
for RAR_FILE in "$(eval \$$RAR_FILES[#])"; do
unrar x -inul "$RAR_FILE" "$DEST_DIR"
if [ $? -gt 0 ]; then
echo $NOW "Error unrarring $TR_TORRENT_NAME" >> $LOG_FILE
transmission-remote -n $TR_USERNAME:$TR_PASSWORD -t$TR_TORRENT_ID --verify --start
exit 0
fi
done
if [[ ! "$TR_TORRENT_PARAMETER" =~ "KEEP" ]]; then
SLEEP=$(expr match "$TR_TORRENT_PARAMETER" '.*SLEEP\([0-9a-zA-Z]*\)')
if [ ${#SLEEP} -gt 0 ]; then
sleep $SLEEP
fi
transmission-remote -n $TR_USERNAME:$TR_PASSWORD -t$TR_TORRENT_ID --remove-and-delete
fi
echo $NOW "Unrarred $TR_TORRENT_NAME" >> $LOG_FILE
fi
fi
fi
} &
(i had some trouble with indirect references, i hoped i fixed that correctly)
Well for the $VARIABLE =~ PATERN you should be able to use the:
echo "$VARIABLE" | grep -E PATTERN
But I think you will have a little bit of trouble with the arithmetical expressions i++ as well - if it's implemented, then you still need to use the i=$(($i + 1)) syntax, if it's not implemented, then the i=$(expr $i + 1) syntax.
I presume you're reason for the IFS=$'\n' is to split the find on newlines, but you're probably better off with issuing the find into a temporary file, and then doing a while read line; do ... done <$tmpfile,
Additionally, I'm not certain if all versions of busybox ash support arrays, so you may have a problem there as well.

Resources