Why does `time` not work on `echo`? - time

time seems to work generally on my system:
➜ ~ time touch tmp.txt
touch tmp.txt 0.00s user 0.00s system 54% cpu 0.007 total
Yet its output seems to get suppressed when I attempt to time echo.
➜ ~ time echo 🍔
🍔
And to thicken the plot: it seems to recover its ability to measure echo, provided I throw that echo into a pipeline:
➜ ~ time echo 🍔 | xxd
00000000: f09f 8d94 0a .....
echo 🍔 0.00s user 0.00s system 38% cpu 0.002 total
xxd 0.00s user 0.00s system 88% cpu 0.003 total
What's going on? Is echo outputting to time's favourite stream, replacing its report?
Bonus points for: is there some way (e.g. stream redirection) to make time echo 🍔 output the usual report?
Edit: Some commenters have pointed out that I am using zsh. I'd not noticed that at the time, but that explains a lot.
bash uses normal executables to do time and echo.
bash-3.2$ which echo
/usr/local/opt/coreutils/libexec/gnubin/echo
bash-3.2$ which time
/usr/bin/time
(Looks like I am using a GNU version of echo rather than the presumably BSD one, /bin/echo, that comes with Mac OS X).
Whereas zsh provides something significantly more special:
➜ ~ which echo
echo: shell built-in command
➜ ~ which time
time: shell reserved word
Neither of these are normal executables. Very interesting!

This seems to happen when you use the built-in time (which measures a complete pipeline) combined with another built-in command, like echo, true.
Curiously, I could not find any mention of this in the zshall man page.
To work around it, run this:
time command echo 🍔
The command precommand modifier invokes the external echo instead of the built-in one.

Related

How to invoke time command with parameter and same behavior as without parameter?

I want to specify the format for time, but it behaves differenty:
$ time echo hehe | sleep 1
real 0m1.03s
user 0m0.00s
sys 0m0.00s
$ time -p echo hehe | sleep 1
real 0.00
user 0.00
sys 0.00
So if I provide any switch to time, it will behave differently:
in first case the time measured the whole commnad's time (with pipe)
in the second case it measured only the echo's time
How can I specify -p and get the result from the first run?
You can't. In ksh, time can time a pipeline, while time -p can only time the first stage.
POSIX time on a pipeline is undefined. ksh's implementation chooses to time the entire pipeline.
ksh time does not support -p or any other flags, and will delegate to external time if it's invoked with any flags.
external time (e.g. /usr/bin/time) can only operate on the first stage of a pipeline (because pipelines are a shell construct and require shell cooperation).
Either switch to a shell that does allow this (like bash), or rewrite to something that external time can work with, such as a single stage with sh -c that internally runs a pipeline:
time -p sh -c 'echo hehe | sleep 1'

Parallel processing or threading in Shell scripting

I am writing a script in shell in which a command is running and taking 2 min. everytime. Also, there is nothing we can do with this. But if i want to run this command 100 times in script then total time would be 200min. and this will create a big issue. Nobody want to wait for 200min. What i want is to run all 100 commands parallely so that output will come in 2min or may be some more time but dont take 200min.
it will be appreciated, if any body can help me on this in any way.
GNU Parallel is what you want, unless you want to reinvent the wheel. Here are some more detailed examples, but the short of it:
ls | parallel gzip # gzip all files in a directory
... run all 100 commands parallely so that output will come in 2min
This is only possible if you have 200 processors on your system.
There's no such utility/command in shell script to run commands in parallel. What you can do is run your command in background:
for ((i=0;i<200;i++))
do
MyCommand &
done
With & (background), each execution is scheduled as soon as possible. But this doesn't guarantee that your code will be executed in less 200 min. It depends how many processors are there on your system.
If you have only one processor and each execution of the command (that takes 2min) is doing some computation for 2 min, then processor is doing some work, meaning there's no cycles wasted. In this case, running the commands in parallel is not going help because, there's only one processor which is also not free. So, the processes will be just waiting for their turn to be executed.
If you have more than one processors, then the above method (for loop) might help in reducing the total execution time.
As #KingsIndian said, you can background tasks, which sort of lets them run in parallel. Beyond this, you can also keep track of them by process ID:
#!/bin/bash
# Function to be backgrounded
track() {
sleep $1
printf "\nFinished: %d\n" "$1"
}
start=$(date '+%s')
rand3="$(jot -s\ -r 3 5 10)"
# If you don't have `jot` (*BSD/OSX), substitute your own numbers here.
#rand3="5 8 10"
echo "Random numbers: $rand3"
# Make an associative array in which you'll record pids.
declare -A pids
# Background an instance of the track() function for each number, record the pid.
for n in $rand3; do
track $n &
pid=$!
echo "Backgrounded: $n (pid=$pid)"
pids[$pid]=$n
done
# Watch your stable of backgrounded processes.
# If a pid goes away, remove it from the array.
while [ -n "${pids[*]}" ]; do
sleep 1
for pid in "${!pids[#]}"; do
if ! ps "$pid" >/dev/null; then
unset pids[$pid]
echo "unset: $pid"
fi
done
if [ -z "${!pids[*]}" ]; then
break
fi
printf "\rStill waiting for: %s ... " "${pids[*]}"
done
printf "\r%-25s \n" "Done."
printf "Total runtime: %d seconds\n" "$((`date '+%s'` - $start))"
You should also take a look at the Bash documentation on coprocesses.

Using time in a bash script: how to get the "normal" output?

If I use time in the shell, I get an output like
$ time sleep 1
real 0m1.003s
user 0m0.000s
sys 0m0.000s
However, if I use the same command in a bash script, the output is this
0.00user 0.00system 0:01.00elapsed 0%CPU (0avgtext+0avgdata 2176maxresident)k
0inputs+0outputs (0major+179minor)pagefaults 0swaps
I already found this post, but what I want is to use time in a script, while still getting the same output as if I was using it in a shell.
0.00user 0.00system 0:01.00elapsed 0%CPU (0avgtext+0avgdata 2176maxresident)k
0inputs+0outputs (0major+179minor)pagefaults 0swaps
...is the default output produced by the GNU version of the external time command.
real 0m1.003s
user 0m0.000s
sys 0m0.000s
...is the default output produced by the bash builtin time. This builtin version of time is a bash extension: a POSIX-compliant shell is not required to provide a builtin version of time.
However, POSIX does specify an external time utility, which can take -p option to produce yet another output format:
real 1.01
user 0.00
sys 0.00
...and the bash builtin also accepts the -p option to produce the same output format.
The bash builtin should work perfectly well in a shell script provided the script is actually being run by bash:
$ cat time.sh
#!/bin/bash
time sleep 1
$ ./time.sh
real 0m1.001s
user 0m0.000s
sys 0m0.000s
$
So it seems that your script is invoking the external utility rather than the bash builtin.
The most likely cause of this is that:
your script says #!/bin/sh rather than #!/bin/bash; and
the /bin/sh on your machine is not actually bash, but a more lightweight POSIX-compliant shell without the bash extensions (as found on some Linux distributions these days).
The solution is to ensure that the script specifically invokes bash by using #!/bin/bash if it relies on bash extensions.
Did you try the -p flag?
$ echo "time -p sleep 1" > x1
$ bash x1
real 1.01
user 0.00
sys 0.00
The difference is that output is not going to terminal when piped.
The terminal codes necessary to do the nice layout do not work on a filestream.
What did you want to achieve? If you intend to have the 'screenshot' have a look at script

Shell script to calculate time elapsed

I was trying to write a script to execute a C program and measure the time for execution using time command in Unix.
The script looks like follows
cc /home/pop/Daa/abc.c
r = `time /home/pop/Daa/./a.out`
echo "recursion" $r >> log.txt
cc /home/pop/Daa/xyz.c
d = `time /home/pop/Daa/./a.out `
echo "dynamic" $d >> log.txt
But after executing the script the log.txt file contains only words recursion and dynamic. The time values seem to be missing. But executing time command on the commandline terminal gave the following output
real 0m0.001s
user 0m0.000s
sys 0m0.000s
How can get this output formatted to contain only the 'real' time of execution and written in the log file?
The output of time goes to stderr. You have to redirect it.
TIMEFORMAT=%R
r=$( { time /home/pop/Daa/./a.out; } 2>&1 )
And you can't have spaces around the equal sign. It is preferable to use $() instead of backticks since it's easier to read, there's no confusion with single quotes and it's easier to nest them.
When you assign values to a variable you shouldn't have spaces around the equals sign. For example:
r=`time /home/pop/Daa/./a.out`
If you only want the "real" time, use grep:
r=`time /home/pop/Daa/./a.out | grep real`
or use %e in the time command:
r=`time -f '%e' /home/pop/Daa/./a.out`

How do I get just real time value from 'time' command?

I would like to make a script that outputs only the real time value from the time command so I can plot the results. For example time command outputs
real 1m0.001s
user 1m0.000s
sys 0m0.001s
I want to write a script that outputs
60.001
How do I get just real time value from 'time' command in seconds?
If you're using the Bash builtin time, set the TIMEFORMAT variable to %R:
$ TIMEFORMAT=%R
$ time sleep 1
1.022
time can take an optional --format or -f parameter, but you have to use the full path to the time command, /usr/bin/time
/usr/bin/time -f "%e" sleep 3
3.00
Without the path, it'll use the time command of the shell which just treats all arguments as the command, so you'll get an -f: command not found.
If you write \time you enforce not to use the bash built in time.
So with \time -f '%e' command you are done.
Alternatively, use /usr/bin/time or take a look at the similar question
Using time command in bash script.

Resources