1

using:
Ubuntu 20.04.4 LTS (Focal Fossa)
GNU bash, version 5.1.16(1)
and
script --version
script from util-linux 2.34

These commands are working from a terminal prompt:

script /home/x/Desktop/clamlog2.cat ; # Log file start.
script --version ; # 2.34 version number, for clarity.
bash /media/x/usb_stick/clamscan.sh ; # virus scan script.
ls ; # example of other commands to Log.
exit ; # Log file end.
cat /home/x/Desktop/clamlog2.cat ; # review Log file in colour.

How to get above terminal prompt commands working
from a bash script?

Purpose of above commands?
To make a colour Log file of a scan. ( Clam AntiVirus: Scanner 0.103.6 )
Colour Log means not text but text with colour as you see it from the prompt.
Colour Log file = /home/x/Desktop/clamlog2.cat

Problem:
bash script stops after first line:
script /home/x/Desktop/clamlog2.cat ; # Log file start.
saying:
Script started, file is /home/x/Desktop/clamlog2.cat

Above commands work from terminal prompt.
How to automate?
How to get above commands working in a bash script?

--

0

2 Answers 2

0

Explanation

Lines in a shell script do not simulate keyboard input. Each statement is a command that must finish before the next is run, and the entire thing runs in the context of the parent shell.

In an interactive shell you can type script mylogEnter, this gives you another (inner) shell and then you can type e.g. lsEnter which executes ls in the inner shell. Eventually you can exit the inner shell by typing exitEnter. When script sees the other shell exits, it exits. When the original (outer) shell sees script exits, it continues: it shows you the prompt and reads what you type. The next command (e.g. cat mylog) will be executed in the original shell.

As long as script runs, the outer shell waits and does not try to read from the terminal (in general: from stdin; in case of an interactive shell stdin is often the terminal). script reads from the terminal and relays input to the inner shell.

The same sequence of commands in a script being executed will make the shell executing the script run script mylog and wait for it to exit, just like in the case of interactive usage. And just like in the case of interactive usage, the inner shell will run commands relayed by script, i.e. whatever script reads from its stdin (which is usually the terminal). The important difference is: the outer shell reads code from the regular file (i.e. not from stdin), but the inner shell can only get code from stdin. The inner shell has no access to the script.

You claim your script stops after script /home/x/Desktop/clamlog2.cat. It does not stop. It waits for script to exit; and script waits for the inner shell to exit; and the inner shell waits for you to type something. The prompt from the inner shell most likely looks exactly like the prompt from the interactive shell you started from, so you may think the whole script has exited. No, it has not; you're in the inner shell inside script inside the outer shell (interpreting your script file) and the original interactive shell is even more outer. Now if you type exit then the script will continue and commands you intended for the inner shell will be executed by the shell interpreting the script, including the exit line (cat … will never be executed).


Flawed "solution"

By providing the script via stdin of the outer shell, you can kinda simulate keyboard input. Like this:

# But don't.
bash <./the_script

This bash will read code from its stdin and when it invokes script …, the tool will inherit the stdin, so by reading its stdin it will also read from the file. This method is flawed because:

  • bash can and sometimes will (and in some cases must) read the file beyond script … before it invokes script. In effect script may read the file (i.e. the inner shell may get the code) starting from a point you don't expect. After it exits, the shell interpreting the script will execute already read code it shouldn't have read in the first place.
  • Even if bash reads exactly what you hope for, script will probably read beyond exit (in our case it will read the cat … line). The inner shell will get exit and it will exit as expected, but the outer shell won't see what script has read excessively (cat … won't be executed at all; neither the inner shell nor the outer shell will see it).

Solution: here document

It's way better to run the_script normally. Inside it you can run script with redirected stdin. This example the_script uses a here document for redirection:

#!/bin/sh
script mylog <<EOF
ls
date
EOF
cat mylog

Now script can read from the here document only (and you don't need exit, the sole end of the here document should be enough). Note if instead of (or alongside) ls or date you had a command that reads from its stdin (e.g. another script or ffmpeg) then the problem of some tool reading too much would appear, like in the flawed "solution" above; it would be about reading too much from the here document, not from the entire script, but still.

If what you want to run inside script does not read from stdin, or if you properly redirect stdin of whatever wants to read, then a here document may be a good solution.


Solution: script --command

Another solution is to provide code (to run in the inner shell) as an argument to script. From man 1 script:

-c, --command command
Run the command rather than an interactive shell. This makes it easy for a script to capture the output of a program that behaves differently when its stdout is not a tty.

It's not explicitly stated, but command is interpreted as shell code (i.e. not necessarily a single executable). Example:

#!/bin/sh
script mylog -c '
  ls
  date
'
cat mylog

Note the inner shell interpreting ls; date is not an interactive shell. It won't print prompts and probably your aliases won't work.


Final note

There are other tools that give you a shell or a shell-like interface, or just read from stdin for whatever reason. Imagine instead of script … you have bash, ssh user@server, python3 or even cat. If you invoke any of it from a shell script, don't expect the next line(s) of the script to get to the tool. The mechanism is exactly like explained, it's not specific to script. Our solution using a here document is universal. Our solution using script -c is obviously specific to script, but other tools may support a similar way to provide code/input as a command line argument (e.g. bash -c code, ssh user@server code).

0

Adding to Kamil's answer ...

#!/bin/sh
script /home/x/Desktop/clamlog2.cat -c '
clear # reduce scroll-back text / screens.
bash /media/x/usb_stick/clamscan.sh # virus scan script.
echo "Ctrl-Shift-UParrow # keyboard shortcut scroll-back reminder."
'
cat /home/x/Desktop/clamlog2.cat

Kamil's answer of
the original (outer) shell versus inner shell ...
reminds me of Star Trek The Next Generation Professor Moriarty
Elementary, Dear Data

Professor Moriarty Call For the Holodeck's Arch
https://www.youtube.com/watch?v=msjQKkkW2Wo

Captain Picard...
Computer End Program.
...
Computer End Program.
Computer ...

2
  • As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
    – Community Bot
    Commented Jun 18, 2022 at 14:12
  • To add clarity, the question How to get bash script working? is stated in above bash script, specifically, the 4 characters -c ' in the command script /home/x/Desktop/clamlog2.cat -c ' and then later 1 character ' to close it off thus above script deals with the issue of the original (outer) shell versus inner shell via script version 2.34 and also to get a colour Log on screen then cat the Log file meaning cat /home/x/Desktop/clamlog2.cat or Kamil's answer (e.g. cat mylog ).
    – joseph22
    Commented Jun 18, 2022 at 16:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .