I'd like to use time command in a bash script to calculate the elapsed time of some function, but time command gives me error messages when I try to call it to measure one of the script's functions using any of its options, like '--format', which I want to make use of.
The same thing happens if I use 'time [...]' from terminal, with any command. However, using it from terminal, it works when I enter '/usr/bin/time [...]' instead of just 'time [...]', but when I call '/usr/bin/time ANY_FUNCTION_INSIDE_ANY_SCRIPT [arguments]' in a script, it complains about the called function being not a valid command.
test.sh code:
#!/bin/bash
testime ()
{
printf "test message is \"$1\" \n"
printf "just testing...\n"
sleep 5
read -p "enter something: " buffer
echo $buffer
}
time -f "testime() finished after %E" testime "damn!"
printf "Bye bye!\n\n"
exit
Output:
$ ./test.sh
./test.sh: line 12: -f: command not found
real 0m0.001s
user 0m0.000s
sys 0m0.001s
Bye bye!
BUT... If i use time without options, like "-f ...", as here:
[...]
time testime "damn!"
printf "Bye bye!\n\n"
exit
...the script works correctly, as you can see:
$ ./test.sh
test message is "damn!"
just testing...
enter something: SOMETHING_I_HAVE_ENTERED
SOMETHING_I_HAVE_ENTERED
real 0m24.050s
user 0m0.000s
sys 0m0.001s
Bye bye!
AND when I call '/usr/bin/time -f [...]', as it would work when I'm doing stuff from terminal, using '-f [...]' or not, it gives me some output like this:
$ ./test.sh
/usr/bin/time: cannot run testime: No such file or directory
Command exited with non-zero status 127
0.00user 0.00system 0:00.00elapsed ?%CPU (0avgtext+0avgdata 340maxresident)k
0inputs+0outputs (0major+72minor)pagefaults 0swaps
Bye bye!
Does someone know why it happens?
Mention the command and -f option at the end.
Shell> time testime 'damn!' -f 'testime() finished after %E'
test message is "damn!"
just testing...
enter something: abc
abc
real 0m7.513s
user 0m0.000s
sys 0m0.003s
The usage of the test command clearly mentioned that you need to give command before to options.
NAME
time - time a simple command or give resource usage
SYNOPSIS
time [options] command [arguments...]
In [options] only -P is given and remaining are arguments.
Related
I recently installed the developer beta of macOS 10.15 Catalina, which defaults to ZSH instead of BASH.
I have time installed as a binary in /usr/bin/time, and use that to time some of my scripts. ZSH, however, has this as a built-in command. My problem is that I usually use time -p to format its output in a way I prefer, but that doesn't seem to be possible on ZSH.
According to man time:
-p The output is formatted as specified by IEEE Std 1003.2-1992 (``POSIX.2'').
This doesn't seem to apply to ZSH's built-in, and ZSH doesn't seem to be using my installed time binary:
BASH
bash-5.0$ which time
/usr/bin/time
bash-5.0$ time echo hello; echo $?
hello
real 0m0.000s
user 0m0.000s
sys 0m0.000s
0
bash-5.0$ time -p echo hello; echo $?
hello
real 0.00
user 0.00
sys 0.00
0
ZSH
is-mbp-bleggiero% which time
time: shell reserved word
is-mbp-bleggiero% time echo hello; echo $?
hello
0
is-mbp-bleggiero% time -p echo hello; echo $?
zsh: command not found: -p
-p echo hello 0.00s user 0.00s system 74% cpu 0.001 total
127
And that looks odd to me; it seems to be including the info I want (user vs system & total, formatted as seconds), but it also doesn't run the command, instead complaining that it doesn't exist.
You can call /usr/bin/time -p if you want to use this option, or =time -p
The zsh builtin time doesn't take any option (man zshmisc).
But you can export TIMEFMT with the format you want, including newlines with \n.
echo $TIMEFMT to see what it is now
I was trying to compose a command that would monitor the stability of the script on server by curling it every couple of minutes (actual path to script was replaced):
while :; do date +"%T" >> monitor.txt; time curl -Is http://googel.com | egrep "HTTP|m.\." >> monitor.txt; echo ================ >> monitor.txt; sleep 30; done
The problem is that for some reason part of output is not forwarded to file monitor.txt. So file contains following lines:
$ cat monitor.txt
19:39:10
HTTP/1.1 301 Moved Permanently
================
19:39:40
HTTP/1.1 301 Moved Permanently
================
..while time details go to default output:
$ while :; do date +"%T" >> monitor.txt; time curl -Is http://googel.com | egrep "HTTP|m.\." >> monitor.txt; echo ================ >> monitor.txt; sleep 30; done
real 0m0.075s
user 0m0.005s
sys 0m0.003s
real 0m0.106s
user 0m0.004s
sys 0m0.005s
Could you please point out, what am I missing here? Basically I would run this command in a background and check monitor.txt for results.
Thank you in advance!
The time command sends its output to stderr, not stdout. Your redirection only affects stdout, so the time output ends up going
to the console.
To add to the confusion, bash also has a builtin time command, which
is a bit trickier to redirect. If you use /usr/bin/time instead of
time, you should be able to redirect its output with the 2>&1 syntax.
Or if you prefer the bash builtin version of the command, you can see this answer for a way
to redirect its output.
Just a little question about timing programs on Linux: the time command allows to
measure the execution time of a program:
[ed#lbox200 ~]$ time sleep 1
real 0m1.004s
user 0m0.000s
sys 0m0.004s
Which works fine. But if I try to redirect the output to a file, it fails.
[ed#lbox200 ~]$ time sleep 1 > time.txt
real 0m1.004s
user 0m0.001s
sys 0m0.004s
[ed#lbox200 ~]$ cat time.txt
[ed#lbox200 ~]$
I know there are other implementations of time with the option -o to write a file but
my question is about the command without those options.
Any suggestions ?
Try
{ time sleep 1 ; } 2> time.txt
which combines the STDERR of "time" and your command into time.txt
Or use
{ time sleep 1 2> sleep.stderr ; } 2> time.txt
which puts STDERR from "sleep" into the file "sleep.stderr" and only STDERR from "time" goes into "time.txt"
Simple. The GNU time utility has an option for that.
But you have to ensure that you are not using your shell's builtin time command, at least the bash builtin does not provide that option! That's why you need to give the full path of the time utility:
/usr/bin/time -o time.txt sleep 1
Wrap time and the command you are timing in a set of brackets.
For example, the following times ls and writes the result of ls and the results of the timing into outfile:
$ (time ls) > outfile 2>&1
Or, if you'd like to separate the output of the command from the captured output from time:
$ (time ls) > ls_results 2> time_results
If you care about the command's error output you can separate them like this while still using the built-in time command.
{ time your_command 2> command.err ; } 2> time.log
or
{ time your_command 2>1 ; } 2> time.log
As you see the command's errors go to a file (since stderr is used for time).
Unfortunately you can't send it to another handle (like 3>&2) since that will not exist anymore outside the {...}
That said, if you can use GNU time, just do what #Tim Ludwinski said.
\time -o time.log command
Since the output of 'time' command is error output, redirect it as standard output would be more intuitive to do further processing.
{ time sleep 1; } 2>&1 | cat > time.txt
If you are using GNU time instead of the bash built-in, try
time -o outfile command
(Note: GNU time formats a little differently than the bash built-in).
&>out time command >/dev/null
in your case
&>out time sleep 1 >/dev/null
then
cat out
I ended up using:
/usr/bin/time -ao output_file.txt -f "Operation took: %E" echo lol
Where "a" is append
Where "o" is proceeded by the file name to append to
Where "f" is format with a printf-like syntax
Where "%E" produces 0:00:00; hours:minutes:seconds
I had to invoke /usr/bin/time because the bash "time" was trampling it and doesn't have the same options
I was just trying to get output to file, not the same thing as OP
If you don't want to touch the original process' stdout and stderr, you can redirect stderr to file descriptor 3 and back:
$ { time { perl -le "print 'foo'; warn 'bar';" 2>&3; }; } 3>&2 2> time.out
foo
bar at -e line 1.
$ cat time.out
real 0m0.009s
user 0m0.004s
sys 0m0.000s
You could use that for a wrapper (e.g. for cronjobs) to monitor runtimes:
#!/bin/bash
echo "[$(date)]" "$#" >> /my/runtime.log
{ time { "$#" 2>&3; }; } 3>&2 2>> /my/runtime.log
#!/bin/bash
set -e
_onexit() {
[[ $TMPD ]] && rm -rf "$TMPD"
}
TMPD="$(mktemp -d)"
trap _onexit EXIT
_time_2() {
"$#" 2>&3
}
_time_1() {
time _time_2 "$#"
}
_time() {
declare time_label="$1"
shift
exec 3>&2
_time_1 "$#" 2>"$TMPD/timing.$time_label"
echo "time[$time_label]"
cat "$TMPD/timing.$time_label"
}
_time a _do_something
_time b _do_another_thing
_time c _finish_up
This has the benefit of not spawning sub shells, and the final pipeline has it's stderr restored to the real stderr.
If you are using csh you can use:
/usr/bin/time --output=outfile -p $SHELL -c 'your command'
For example:
/usr/bin/time --output=outtime.txt -p csh -c 'cat file'
If you want just the time in a shell variable then this works:
var=`{ time <command> ; } 2>&1 1>/dev/null`
Is there a way to force command substitution to exit leaving baground task undone?
Example:
cat test.sh
#! /bin/bash
sleep 5 &
echo "foo bar"
Running it without command substitution:
time bash test.sh
foo bar
real 0m0.005s
user 0m0.005s
sys 0m0.000s
With command substitution:
time echo $(bash test.sh)
foo bar
real 0m5.004s
user 0m0.002s
sys 0m0.000s
You need to redirect stdout and stderr for sleep:
#! /bin/bash
sleep 5 >/dev/null 2>&1 &
echo "foo bar"
bash command substitution on a script that executes a background task
I have a shell script I use for deployments. Since I want to capture the output of the entire process, I've wrapped it in a subshell and tail that out:
#! /usr/bin/env ksh
# deploy.sh
########################################################################
(yadda, yadda, yadda)
########################################################################
# LOGGING WRAPPER
#
dateFormat=$(date +"%Y.%m.%d-%H.%M.%S")
(
print -n "EXECUING: $0 $*: "
date
#
########################################################################
(yadda, yadda, yadda)
#
# Tail Startup
#
trap 'printf "Stopping Script: ";date;exit 0"' INT
print "TAILING LOG: YOU MAY STOP THIS WITH A CTRL-C WHEN YOU SEE THAT SERVER HAS STARTED"
sleep 2
./tailLog.sh
) 2>&1 | tee "deployment.$dateFormat.log"
#
########################################################################
Before I employed the subshell, the trap command worked. When you pressed CNTL-C, the program would print Stopping Script: and the date.
However, I wanted to make sure that no one forgets to save the output of this script, so I employed the subshell to automatically save the output. And, now trap doesn't seem to be working.
What am I doing wrong?
NEW INFORMATION
A little more playing around. I now see the issue isn't the shell or subshell. It's the damn pipe!
If I don't pipe the output to tee, the trap works fine. If I pipe the output to tee, the trap doesn't work.
So, the real question is how do I tee the output and still be able to use trap?
TEST PROGRAM
Before you answer, please, please, try these test programs:
#! /bin/ksh
dateFormat=$(date +"%Y.%m.%d-%H:%M:%S")
(
trap 'printf "The script was killed at: %s\n", "$(date)"' SIGINT
echo "$0 $*"
while sleep 2
do
print -n "The time is now "
date
done
) | tee somefile
And
#! /bin/ksh
dateFormat=$(date +"%Y.%m.%d-%H:%M:%S")
(
trap 'printf "The script was killed at: %s\n", "$(date)"' SIGINT
echo "$0 $*"
while sleep 2
do
print -n "The time is now "
date
done
)
The top one pipes to somefile..... The bottom one doesn't. The bottom one, the trap works. The top one, the trap doesn't. See if you can get the pipe to work and the "The script was killed at" line to print into the teed out file.
The pipe does work. The trap doesn't, but only when I have the pipe. You can move the trap statement all around and put in layers and layers of sub shells. There's some minor thing I am doing that's wrong, and I have no idea what it is.
Since trap stops the running process – logShell.sh – I think the pipe doesn't get executed at all. You can't do it this way.
One solution could be editing logShell.sh to write line by line in your log file. Maybe you could post it and we can discuss how you manage it.
OK, now I've got it. You have to use tee with -i to ignore interrupt signals.
#! /bin/ksh
dateFormat=$(date +"%Y.%m.%d-%H:%M:%S")
(
trap 'printf "The script was killed at: %s\n", "$(date)"' SIGINT
echo "$0 $*"
while sleep 2
do
print -n "The time is now "
date
done
) | tee -i somefile
this one works fine!