I have two programs, prog1 and prog2, prog2 takes the PID of prog1 as argument and they have to run in parallel.
I need stdin and stdout of prog1 to stay connected to the shell, note that it's not an interactive shell.
I tried like this, but stdin of prog1 is not connected to the shell:
#!/bin/bash
./prog1 & (./prog2 $! 1>&2 0<&-)
fg
From bash manual:
If a command is terminated by the control operator ‘&’, the shell executes the command asynchronously in a subshell. This is known as executing the command in the background, and these are referred to as asynchronous commands. The shell does not wait for the command to finish, and the return status is 0 (true). When job control is not active (see Job Control), the standard input for asynchronous commands, in the absence of any explicit redirections, is redirected from /dev/null.
So let's make some explicit redirections! The following scripts:
==> prog1 <==
#!/bin/bash
set -x
read data
echo "$data"
==> prog2 <==
#!/bin/bash
while kill -0 "$1"; do sleep 0.1; done
==> script.sh <==
#!/bin/bash
set -x
./prog1 <&0 &
# ^^^^ - the important part
./prog2 $! >&2 <&-
wait
When executing ./script.sh results in:
$ LC_ALL=C ./script.sh
+ ./prog2 1835980
+ ./prog1
+ read data
bla
+ echo bla
bla
./prog2: line 2: kill: (1835980) - No such process
+ wait
Suppose I have a program foo which prints to both stderr and stdout. I want to be able to time it and tee the output to a log file.
Program Description
I can call foo as follows:
user$ time ./foo
This gives (for example) output on two streams:
stdout:
stdout says hi
stderr:
stderr says hi
Combining stderr and stdout
I can combine them easily enough to standard output using BASH I/O redirection:
user$ ./foo 2>&1
This gives the following output:
stdout:
stdout says hi
stderr says hi
stderr: (empty!)
Using tee
I can capture the combined output of the program using tee:
user$ ./foo 2>&1 | tee log.txt
stdout:
stdout says hi
stderr says hi
stderr: (empty!)
log.txt:
stdout says hi
stderr says hi
Capturing Timing using time
I can time the execution of my program using BASH's builtin time command while combining stdout and stderr:
user$ time ./foo 2>&1
stdout:
stdout says hi
stderr says hi
stderr::
real 0m0.017s
user 0m0.003s
sys 0m0.004s
I can capture the output of the time command as described here and redirect it to a file:
user$ ( time ./foo ) > log.txt
stdout: (empty!)
stderr:
stderr says hi
real 0m0.017s
user 0m0.003s
sys 0m0.004s
log.txt:
stdout says hi
Combining I/O, Capturing time's output, AND piping to tee
How can this be done? I'm imagining something like the following:
( time ./foo 2&>1 ) | tee log.txt
stdout: (empty!)
stdout says hi
stderr says hi
real 0m0.017s
user 0m0.003s
sys 0m0.004s
stderr: (empty!)
log.txt:
stdout says hi
stderr says hi
real 0m0.017s
user 0m0.003s
sys 0m0.004s
Doing the following works, except when I Ctrl-C (SIGINT) out of the program prematurely.
( time ./foo ) 2&>1 | tee log.txt
How can I get it to work when I Ctrl-C as well?
myfunc() { echo stdout; echo stderr >&2; sleep 3; }
{ time myfunc; } > >(tee log.txt) 2>&1
...emits content including time metadata to log.txt, even when cancelled with Ctrl+C.
See Write tcpdump output to compressed / gziped file for a more-in-depth discussion of the same problem, including an answer intended for scripts as opposed to interactive interpreters.
For the program foo in your example, you could do the following:
{ time ./foo; } > >(tee log.txt) 2>&1
I've come across a behavior I can't explain. When I run a bash script without a shebang, the ps command will not show the script and its arguments as arguments passed to bash, neither will /proc/$$/cmdline, whereas If I run the script with a shebang, behavior is as expected.
Example with a shebang:
# cat mytest
#!/bin/bash
echo my name is $1
cat /proc/$$/cmdline
echo
ps -p $$ -o args=
# ./mytest John
my name is John
/bin/bash./mytestJohn
/bin/bash ./mytest John
Example without a shebang:
# cat mytest
echo my name is $1
cat /proc/$$/cmdline
echo
ps -p $$ -o args=
# ./mytest John
my name is John
-bash
-bash
In both cases the script will display 'my name is John', but without a shebang I see the bash process without any arguments. How is this possible?
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`
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.