using sleep and clear command in bash script - bash

I am learning Bash script.
Inside my script, I've tried clear and sleep 1.
sleep 1 runs as expected but clear runs with command not found error.
Why is that?
clear and sleep 1 are enclosed within backslashes in my script. Stack Overflow is using those symbols to indicate code, and I don't know how to use escape symbols to type them here.

I assume you are trying this :
$(clear)
Or this
`clear`
These are two ways of expression command substitution. The first one is more readable and should be preferred, but they do the same thing.
The solution is to simply use this:
clear
Now, if you are interested in understanding why you are getting this error, here is a longer explanation.
Command substitution captures the output of the command enclosed -- the output of standard output (file descriptor 1), not standard error (standard error), to be more precise -- and then provides this output as a string to be used as part of the command where it was found, as if it had been part of the command to start with (but not subject to further expansions).
The clear command has a special output that causes the terminal screen to clear. But by enclosing clear in backticks, this special output is not sent to the terminal (the terminal does not clear), and it is instead captured by the command substitution. Then, this special output is provided as if it had been typed on the command line, and since it is the first (and only) thing on that line, the shell tries to find a command with a name equal to that special character, which it does not find, and that is where you get the "command not found" error.
Just for fun, try this :
$(clear >&2)
It will clear the screen, and not trigger an error, because the output is redirected to file descriptor 2 (standard error), which is not captured by the command substitution and actually is sent to the terminal (which clears), and since there is no other output, the command substitution evaluates to an empty string, which Bash interprets as a request to do nothing (Bash does not try to find a command with a zero-length name).
I hope this helps you understand the reason you are getting this error.

The backquotes are used in Stack Overflow indicating the a string should be shown as code. Your real code doesn't need backquotes:
echo "Hello from commandline"
sleep 3
echo "After sleeping, will try to use clear in 4 seconds"
sleep 1
echo "will try to use clear in 3 seconds"
sleep 1
echo "will try to use clear in 2 seconds"
sleep 1
echo "will try to use clear in 1 seconds"
sleep 1
clear
When you have seen backquotes in Unix code, you see an old fashioned way to call another command during the execution of a command.
echo "Current time is `date`, so hurry!"
Now we write this as
echo "Current time is $(date), so hurry!"
It is one char more in this simple case, but much better when nesting more things. I will not even try to write the next example with backquotes
echo "Previous command returned $(echo "Current time is $(date), so hurry!")."

Related

Output time to a file with the Unix "time" command, but leave the output of the command to the console

I time a command that has some output. I want to output the real time from the time command to a file, but leave the output of the command to the console.
For example, if I do time my_command I get this printed in the console:
several lines of output from my_command ...
real 1m25.970s
user 0m0.427s
sys 0m0.518s
In this case, I want to store only 1m25.970s to a file, but still print the output of the command to the console.
The time command is tricky. The POSIX specification of time
doesn't define the default output format, but does define a format for the -p (presumably for 'POSIX') option. Note the (not easily understood) discussion of command sequences in pipelines.
The Bash specification say time prefixes a 'pipeline', which means that time cmd1 | cmd2 times both cmd1 and cmd2. It writes its results to standard error. The Korn shell is similar.
The POSIX format requires a single space between the tags such as real and the time; the default format often uses a tab instead of a space. Note that the /usr/bin/time command may have yet another output format. It does on macOS, for example, listing 3 times on a single line, by default, with the label after the time value; it supports -p to print in an approximation to the POSIX format (but it has multiple spaces between label and time).
You can easily get all the information written to standard error into a file:
(time my_command) 2> log.file
If my_command or any programs it invokes reports any errors to standard error, those will got to the log file too. And you will get all three lines of the output from time written to the file.
If your shell is Bash, you may be able to use process substitution to filter some of the output.
I wouldn't try it with a single command line; the hieroglyphs needed to make it work are ghastly and best encapsulated in shell scripts.
For example, a shell script time.filter to capture the output from time and write only the real time to a log file (default log.file, configurable by providing an alternative log file name as the first argument
#!/bin/sh
output="${1:-log.file}"
shift
sed -E '/^real[[:space:]]+(([0-9]+m)?[0-9]+[.][0-9]+s?)/{ s//\1/; w '"$output"'
d;}
/^(user|sys)[[:space:]]+(([0-9]+m)?[0-9]+[.][0-9]+s?)/d' "$#"
This assumes your sed uses -E to enable extended regular expressions.
The first line of the script finds the line containing the real label and the time after it (in a number of possible formats — but not all). It accepts an optional minutes value such as 60m05.003s, or just a seconds value 5.00s, or just 5.0 (POSIX formats — at least one digit after the decimal point is required). It captures the time part and prints it to the chosen file (by default, log.file; you can specify an alternative name as the first argument on the command line). Note that even GNU sed treats everything after the w command as file name; you have to continue the d (delete) command and the close brace } on a newline. GNU sed does not require the semicolon after d; BSD (macOS) sed does. The second line recognizes and deletes the lines reportin the user and sys times. Everything else is passed through unaltered.
The script processes any files you give it after the log file name, or standard input if you give it none. A better command line notation would use an explicit option (-l logfile) and getopts to specify the log file.
With that in place, we can devise a program that reports to standard error and standard output — my_command:
echo "nonsense: error: positive numbers are required for argument 1" >&2
dribbler -s 0.4 -r 0.1 -i data -t
echo "apoplexy: unforeseen problems induced temporary amnesia" >&2
You could use cat data instead of the dribbler command. The dribbler command as shown reads lines from data, writes them to standard output, with a random delay with a gaussian distribution between lines. The mean delay is 0.4 seconds; the standard deviation is 0.1 seconds. The other two lines are pretending to be commands that report errors to standard error.
My data file contained a nonsense 'poem' called 'The Great Panjandrum'.
With this background in place, we can run the command and capture the real time in log.file, delete (ignore) the user and system time values, while sending the rest of standard error to standard error by using:
$ (time my_command) 2> >(tee raw.stderr | time.filter >&2)
nonsense: error: positive numbers are required for argument 1
So she went into the garden
to cut a cabbage-leaf
to make an apple-pie
and at the same time
a great she-bear coming down the street
pops its head into the shop
What no soap
So he died
and she very imprudently married the Barber
and there were present
the Picninnies
and the Joblillies
and the Garyulies
and the great Panjandrum himself
with the little round button at top
and they all fell to playing the game of catch-as-catch-can
till the gunpowder ran out at the heels of their boots
apoplexy: unforeseen problems induced temporary amnesia
$ cat log.file
0m7.278s
(The time taken is normally between 6 and 8 seconds. There are 17 lines, so you'd expect it to take around 6.8 seconds at 0.4 seconds per line.) The blank line is from time; it is pretty hard to remove that blank line, and only that blank line, especially as POSIX says it is optional. It isn't worth it.

greater than symbol at beginning of line

I've just seen the following in a script and I'm not sure what it means:
.............
started=$STATUSDIR/.$EVENT_ID-started
errs=$STATUSDIR/.$EVENT_ID-errors
# started is used to capture the time we started, so
# that it can be used as the latest-result marker for
# the next run...
>"$started"
>"$errs"
# store STDERR on FD 3, then point STDERR to $errs
exec 3>&2 2>"$errs"
..............
Specifically, the ">" at the beginning of the lines. The script actually fails with "No such file or directory". The vars are all supplied via subsidiary scripts and there doesn't seem to be any logic to create the directories it's complaining about.
It's not the easiest thing to Google for, so I thought I'd ask it here so that future bash hackers might find the answers you lovely people are able to provide.
This is a redirection. It's the same syntax used for echo hello >file (or its less-conventional but equally-correct equivalent >file echo hello), just without the echo hello. :)
When attached to an empty command, the effect of a redirection is identical to what it would be with a command that ran and immediately exited with no output: It creates (if nonexistent) or truncates (if existent) the output file, closes that file, and lets the script move on to the next command.

Setting shell's control break statement in the standard input stream "EOD" to exit the COBOL program

I would like to invoke a cobol program thru shell script by assigning command line prompt values in the "EOD" as below.
#!/bin/bash
run pub/coblprog<<:EOD:
1
2
3
:EOD:
Consider if COBOL program "coblprog" has 4 command line prompts and expects 4 input command line arguments at runtime. I have specified 3 prompt values in the EOD. Since COBOL has four prompts but at EOD does passing 3 values , COBOL program is going into infinity loop to expecting the fourth prompt value.
My requirement is, I would like to set an shell's control break statement (like below) after all prompt values before second :EOD:. By seeing that shell's control statement the the shell script should terminate abnormally.
#!/bin/bash
run pub/coblprog<<:EOD:
1
2
3
exit 1
:EOD:
I have have exit statement in the script and run, but no luck..! Please suggest me good solution.
I am executing the script in LINUX, COBOL program is Micro Focus COBOL.
The data from the line after <<:EOD: until the line just before the one beginning :EOD: is input to the COBOL program, and not shell control statements, therefore the exit 1 will be data, which is probably not what you want.
If you want the shell script to exit after running coblprog, then place it after the line beginning :EOD:.
#!/bin/bash
run pub/coblprog<<:EOD:
1
2
3
4
:EOD:
exit 1
I am not at all familiar with your COBOL interpreter, but if it is at all properly programmed, it should also exit when no more input is available. If that is so, then feeding it three lines of input instead of four should be sufficient. However, it appears that you already tried that.
As a somewhat crude workaround, you could connect it to a FIFO and kill it a while after feeding it the last line of input.
#!/bin/sh
fifo=/tmp/cobolfifo.$$ # insecure, should have more randomness
mkfifo $fifo
cat >>$fifo <<HERE &
1
2
3
HERE
run pub/coblprog <$fifo &
cobol=$!
sleep 5
kill $cobol
rm $fifo
Whether five seconds is sufficient is anybody's guess. If there is any way you could change the COBOL program so that this isn't necessary, that's probably the sanest solution.
OK, not answering questions asked of you, so... shot-in-the-dark time.
Your program is not looping.
It looks like this, doesn't it?
PROCEDURE DIVISION.
ACCEPT VAR1.
ACCEPT VAR2.
ACCEPT VAR3.
ACCEPT VAR4.
I've even coded the pointless dots.
You are stacking up three "user inputs" in your script.
The first ACCEPT takes the first, the second the second, and the third the third.
The fourth ACCEPT finds no data waiting for it, so it waits... for you. To type. It is not looping, it is just waiting for input. You Ctrl-C, and dismiss it as a loop.
If you have four ACCEPTs, you need four entries of data. Either supply four in the script, or supply the fourth "manually", or change the program so that it only ACCEPTs three.

Going backwards in a bash prompt

I'd like to have a blank line after my bash prompt and before the output on my Mac. It should look like this would:
echo; ls
Can I add a newline to my bash prompt and then go back up one line to wait for user input? Is there something obvious I'm missing?
I know this is old but for someone like me who came across this while googling for it. This is how you do this...
It's actually pretty simple!
Check out this link --> Cursor Movement
Basically to move up N number of lines:
echo -e "\033[<N>A HELLO WORLD\n"
Just change the "< N >" to however many lines you want to go back...
For instance, to move up 5 lines it would be "/033[5A"
To my knowledge this is not possible unless you delve into more low-level stuff like full-screen emulators like curses.
This is a bit of a stab in the dark, but you may be able to use VT102 terminal codes to control the cursor without having to use Curses. The relevant VT102 commands that you'd be interested in all consist of sending ESC, then [, then the specific command parameters.
For instance, to move the cursor up one line, one needs to output:
ESC [ 1 A
0x1B 0x5B 0x31 0x41
Be warned that the VT102 documentation generally uses octal, so keep an ascii table handy if you're using hex.
All of this advice is given without having tested it -- I don't know if VT102 commands can be embedded into your bash prompt, but I thought it might be worth a shot.
Edit: Yeah -- looks like a lot of people use VT102 formatting codes in their bash prompts. To translate my above example into something Bash would recognize, putting:
\e[1A
into your prompt should move the cursor up one line.
This is very possible. If your bash has C-v set as the readline quoted-insert command, you can simply add the following to your ~/.inputrc:
RETURN: "\C-e\C-v\n\C-v\n\n"
This wil make bash (readline, actually) insert two verbatim newlines before a regular interpreted newline. By default, only one is inserted, which is what causes output to start on the line after the prompt.
You can test if C-v is set to quoted-insert by typing it in bash (that's Ctrl+V) followed by e.g. an up arrow. This should print ^[[A or something similar. If it doesn't, you can bind it in ~/.inputrc too:
C-v: quoted-insert
RETURN: "\C-e\C-v\n\C-v\n\n"
~/.inputrc can be created if it doesn't exist. The changes will not take effect in running bashes unless you issue a readline re-read-init-file command (by default on C-x C-r). Be careful though. If you do something wrong, enter will no longer issue commands, and fixing your mistake could prove to be difficult. If you should do something wrong, C-o will by default also accept the line.
Adding a newline followed by moving the cursor back to the regular prompt (like you described) is possible, but will not have the effect you intend. The newline you inserted would simply be overwritten by the application output, since you moved the cursor back in front of it.
This works:
trap echo DEBUG
It doesn't add an extra newline if you hit return at an empty prompt.
The command above will cause a newline to be output for every member of a pipeline or multi-command line such as:
$ echo foo; echo bar
\n
foo
\n
bar
To prevent that so that only one extra newline is output before all command output:
PROMPT_COMMAND='_nl=true'; trap -- '$_nl && [[ $BASH_COMMAND != $PROMPT_COMMAND ]] && echo; _nl=false' DEBUG
The DEBUG trap is performed before each command so before the first command it checks to see if the flag is true and, if so, outputs a newline. Then it sets the flag to false so each command afterwards on the line doesn't trigger an extra newline.
The contents of $PROMPT_COMMAND are executed before the prompt is output so the flag is set to true - ready for the next cycle.
Because pressing enter on an empty command line still triggers the execution of the contents of $PROMPT_COMMAND the test in the trap also checks for those contents as the current command and doesn't perform the echo if they match.
I believe (but haven't tried) if you put '\n\b' in the prompt string it would do that.
In general, if you want to find out the codes to do anything a terminal can do, read the terminfo man page.
In this case, the cursor up one line code can be determined by:
tput cuu1
If you redirect the tput output to a file, you can see what control characters are used.
Bash also supports the PROMPT_COMMAND variable, allowing you to run arbitrary commands before each prompt is issued.

C Shell: How to execute a program with non-command line arguments?

My $SHELL is tcsh. I want to run a C shell script that will call a program many times with some arguments changed each time. The program I need to call is in Fortran. I do not want to edit it. The program only takes arguments once it is executed, but not on the command line. Upon calling the program in the script, the program takes control (this is where I am stuck currently, I can never get out because the script will not execute anything until after the program process stops). At this point I need to pass it some variables, then after several iterations I will need to Ctrl+C out of the program and continue with the script.
How can this be done?
To add to what #Toybuilder said, you can use a "here document". I.e. your script could have
./myfortranprogram << EOF
first line of input
second line of input
EOF
Everything between the "<<EOF" and the "EOF" will be fed to the program's standard input (does Fortran still use "read (5,*)" to read from standard input?)
And because I think #ephemient's comment deserves to be in the answer:
Some more tips: <<'EOF' prevents
interpolation in the here-doc body;
<<-EOF removes all leading tabs (so
you can indent the here-doc to match
its surroundings), and EOF can be
replaced by any token. An empty token
(<<"") indicates a here-doc that stops
at the first empty line.
I'm not sure how portable those ones are, or if they're just tcsh extensions - I've only used the <<EOF type "here document" myself.
What you want to use is Expect.
Uhm, can you feed your Fortran code with a redirection? You can create a temporary file with your inputs, and then pipe it in with the stdin redirect (<).
This is a job for the unix program expect, which can nicely and easily interactively command programs and respond to their prompts.
I was sent here after being told my question was close to being a duplicate of this one.
FWIW, I had a similar problem with a csh C shell script.
This bit of code was allowing the custom_command to execute without getting ANY input arguments:
foreach f ($forecastTimes)
custom_command << EOF
arg1=x$f;2
arg2=ya
arg3=z,z$f
run
exit
EOF
end
It didn't work the first time I tried it, but after I backspaced out all of the white space in that section of the code I removed the space between the "<<" and the "EOF". I also backspaced the closing "EOF" all the way to the left margin. After that it worked:
foreach f ($forecastTimes)
custom_command <<EOF
arg1=x$f;2
arg2=ya
arg3=z,z$f
run
exit
EOF
end
Not a tcsh user, but if the program runs then reads in commands via stdin then you can use shell redirection < to feed it the required commands. If you run it in the background with & you will not block when it is executed. Then you can sleep for a bit, then use whatever tools you have (ps, grep, awk, etc) to discover the program's PID, then use kill to send it SIGTERM which is the same as doing a Ctrl-C.

Resources