How does the time module interpret a utility? - bash

The man pages for the time module mention that the command can be used on "utilities".
# time usage from man pages
time [-al] [-h | -p] [-o file] utility [argument ...]
What is meant with "utility" here? Does it simply mean "command"?
If so, I'm confused by the following example, because there are several built-in command present: for, do and done (according to man pages for builtin commands). Am I right to assume that time interprets for i in {1..1000}; do var=${var}foo; done; as one "utility" here? Or does time interpret a "utility" as everything that follows the command if time doesn't appear again, else measuring a new "utility"?
var=; time for i in {1..1000}; do var=${var}foo; done; var=; time for i in {1..1000}; do var+=foo; done

Related

How can I get user+sys returned from time?

I would like to use time to benchmark some code. It seems user+sys is the right measure. But is it possible to get this output as a single number rather than having to add them by hand?
From the documentation of bash's built in time (see help time) and GNU time (see man time) it seems like it is impossible to add those numbers up directly.
You have to manually add the numbers. Here we use GNU time instead of the built-in because it offers the -o option.
command time -f %S+%U -o >(bc -l) yourCommandHere
The last line of the output will be the sum of user time and system time in seconds. command ensures that the binary time (usually /usr/bin/time) is used instead of the bash's built-in time. bc -l adds up the numbers printed by time. As the numbers are floating point we cannot use bash built-ins to do this job.*
* Well, actually you could use bash built-ins when resorting to some hacks:
TIMEFORMAT='10#%3U+10#%3S'
t=$( { time yourCommandHere; } 2>&1 >/dev/null)
printf -v t %04d "$((${t//./}))"
echo "${t:: -3}.${t: -3}"

Cleanly overwrite last output as displayed on the terminal in Bash loop

I have the following loop to view changing dir size:
while true; do du -hcs . ; sleep 2 ; clear ; done
What I'm not happy with is the waiting time between the clear and the actual output of du. The result is more or less a blinking number.
How can I modify this to simply write the new number over the old, with no black screen in between?
watch du -hcs .
From the man page:
watch runs command repeatedly, displaying its output (the first screenfull). This allows you to watch the program output change over time. By default, the program is run every 2 seconds; use -n or --interval to specify a different interval.
watch is smart: it only redraws the parts of the screen that have changed, avoiding the full screen clears that cause the blinking you see with clear.
I think that watch is the correct tool for what you are looking for, but
while true;do echo -n $(date +%Y%m%d%H%M%S) $(du -hcs ..|grep -v total);sleep 1;echo -e "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";done
might be just the poor substitute that you are looking for.
If, however your shell is dash rather than bash, then use:
while true;do echo $(date +%Y%m%d%H%M%S) $(du -hcs ..|grep -v total)|tr -d '\n';sleep 1;echo $'\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b';done

grep behaves strange in combination with inotifywait

I'm having a hard time understanding some sort of anomaly with grep return value.
As noted in the grep man page, the return value will be zero in case of a match and non-zero in case of no-match/error/etc.
In this code: (bash)
inotifywait -m ./logdir -e create -e moved_to |
while read path action file; do
if grep -a -q "String to match" "$path/$file"; then
# do something
fi
done
It returns non-zero when matched.
In this code: (bash)
search_file()
{
if grep -a -q "String to match" "$1"; then
# do something
fi
}
inotifywait -m ./logdir -e create -e moved_to |
while read path action file; do
search_file "$path/$file"
done
It returns zero when matched.
Can someone explain to me what is going on?
EDIT:
Let me be clear once more: if I run the first code on a file that contains the string, the if statement is running. if i run the second code on the same file, the if statement fails and does not run.
I support #John1024's conjecture that he wrote as a comment.
The "anomaly" is likely due to a slight timing difference between the two versions of your script. In case of a create event the file is initially empty, so grep will start scanning a partially written file. Calling grep through a function introduces a small delay, which increases the chances of the searched-for data to appear in the file by the time grep opens the file.
The solution to this race condition depends on a couple of assumptions/requirements:
Can you assume that pre-existing files in the watched directory will not be modified?
Do you want to identify every new matching file as soon as possible, or you can afford delaying its processing until it is closed?

Detect whether script is being run by Bash

I have put together the following to detect if a script is being run by Bash or not:
################################################################################
# Checks whether execution is going through Bash, aborting if it isn't. TinoSino
current_shell="$(
ps `# Report a snapshot of the current processes` \
-p $$ `# select by PID` \
-o comm `# output column: Executable namename` \
|\
paste `# Merge lines of files ` \
-s `# paste one file at a time instead of in parallel` \
- `# into standard output` \
|\
awk `# Pick from list of tokens` \
'{ print $NF }' `# print only last field of the command output`
)"
current_shell="${current_shell#-}" # Remove starting '-' character if present
if [ ! "${current_shell}" = 'bash' ]; then
echo "This script is meant to be executed by the Bash shell but it isn't."
echo 'Continuing from another shell may lead to unpredictable results.'
echo 'Execution will be aborted... now.'
return 0
fi
unset current_shell
################################################################################
I am not asking you specifically to code review it because you would be sending me to CodeReview; my question is:
how would you go about testing whether this "execution guard" put at the top of my script does indeed do its job reliably?
I am thinking about installing Virtual Machines and on each machine to install things like zsh, csh, etc. But it looks way too time-consuming to me. Better ways to do this?
Should you spot an immediate mistake do point it out to me though please. Just glaring bugs waving their legs waiting to be squashed should be squashed, I think.
This is better written as
if [ -z "$BASH_VERSION" ]
then
echo "Please run me in bash"
exit 1
fi
As for testing, get a list of non-bash shells from /etc/shells, and just run the script with each of them verifying that you get your error message.
getshver (recommended)
whatshell
which_interpreter (silly)
I would only recommend rolling your own if it isn't critical to "guarantee" correct results. I don't think such a guarantee is even possible, but most of the time you're at most targeting a few shells and only care about modern versions. Very few people should even care about version detection. Writing portable code while going outside of POSIX requires knowing what you're doing.
Don't bother detecting the shell just to abort. If people want to shoot themselves in the foot by ignoring the shebang that's their problem.

Shell script takes a list of commands as input, tries to execute them, and fails

I am, like many non-engineers or non-mathematicians who try writing algorithms, an intuitive. My exact psychological typology makes it quite difficult for me to learn anything serious like computers or math. Generally, I prefer audio, because I can engage my imagination more effectively in the learning process.
That said, I am trying to write a shell script that will help me master Linux. To that end, I copied and pasted a list of Linux commands from the O'Reilly website's index to the book Python In a Nutshell. I doubt they'll mind, and I thank them for providing it. These are the textfile `massivelistoflinuxcommands,' not included fully below in order to save space...
OK, now comes the fun part. How do I get this script to work?
#/bin/sh
read -d 'massivelistoflinuxcommands' commands <<EOF
accept
bison
bzcmp
bzdiff
bzgrep
bzip2
bzless
bzmore
c++
lastb
lastlog
strace
strfile
zmore
znew
EOF
for i in $commands
do
$i --help | less | cat > masterlinuxnow
text2wave masterlinuxnow -o ml.wav
done
It really helps when you include error messages or specific ways that something deviates from expected behavior.
However, your problem is here:
read -d 'massivelistoflinuxcommands' commands <<EOF
It should be:
read -d '' commands <<EOF
The delimiter to read causes it to stop at the first character it finds that matches the first character in the string, so it stops at "bzc" because the next character is "m" which matches the "m" at the beginning of "massive..."
Also, I have no idea what this is supposed to do:
$i --help | less | cat > masterlinuxnow
but it probably should be:
$i --help > masterlinuxnow
However, you should be able to pipe directly into text2wave and skip creating an intermediate file:
$i --help | text2wave -o ml.wav
Also, you may want to prevent each file from overwriting the previous one:
$i --help | text2wave -o ml-$i.wav
That will create files named like "ml-accept.wav" and "ml-bison.wav".
I would point out that if you're learning Linux commands, you should prioritize them by frequency of use and/or applicability to a beginner. For example, you probably won't be using bison right away`.
The first problem here is that not every command has a --help option!! In fact the very first command, accept, has no such option! A better approach might be executing man on each command since a manual page is more likely to exist for each of the commands. Thus change;
$i --help | less | cat > masterlinuxnow
to
man $i >> masterlinuxnow
note that it is essential you use the append output operator ">>" instead of the create output operator ">" in this loop. Using the create output operator will recreate the file "masterlinuxnow" on each iteration thus containing only the output of the last "man $i" processed.
you also need to worry about whether the command exists on your version of linux (many commands are not included in the standard distribution or may have different names). Thus you probably want something more like this where the -n in the head command should be replace by the number of lines you want, so if you want only the first 2 lines of the --help output you would replace -n with -2:
if [ $(which $i) ]
then
$i --help | head -n >> masterlinuxnow
fi
and instead of the read command, simply define the variable commands like so:
commands="
bison
bzcmp
bzdiff
bzgrep
bzip2
bzless
bzmore
c++
lastb
lastlog
strace
strfile
zmore
znew
"
Putting this all together, the following script works quite nicely:
commands="
bison
bzcmp
bzdiff
bzgrep
bzip2
bzless
bzmore
c++
lastb
lastlog
strace
strfile
zmore
znew
"
for i in $commands
do
if [ $(which $i) ]
then
$i --help | head -1 >> masterlinuxnow 2>/dev/null
fi
done
You're going to learn to use Linux by listening to help descriptions? I really think that's a bad idea.
Those help commands usually list every obscure option to a command, including many that you will never use-- especially as a beginner.
A guided tutorial or book would be much better. It would only present the commands and options that will be most useful. For example, that list of commands you gave has many that I don't know-- and I've been using Linux/Unix extensively for 10 years.

Resources