Shell Run Box using only script / term

Hi All,

A few years ago and with quite a lot of help from the forums I created my own 'Run Box' [can be seen at bottom of this screen shot]


It had fairly low requirement, just a short script which waited for user input then executed the given command, called from within a small xterm window as shown.

I know some terminal programs can execute a command and exit themselves with a simple switch (-e?) but I would struggle with the user input stuff coming back to this after all this time.

Any pointers on where to get started or script examples would be appreciated!

Thanks guys :)
 
Thanks for replies so far. I want to do this in as little resource as possible. I have virtually no scripting skills but can learn from examples. Wouldn't that xterm line persist once the command was run?
 
Yes, and it persists until exit. It also shows the prompt which my original bash didnt. I'm trying to re-create something like fbrun or similar. Need to figure how to wait for input, then execute that input, then quit.

xterm -e command is fine for the x part - but its the user input and how to pass that back to xterm ... hmmm...
 
and it persists until exit.
The process runs until it exits. Persistence is something else entirely. You may be somewhat familiar with the old DOS TSR (Terminate-and-Stay-Resident) processes. But that works in a completely different way (good old DOS wasn't multi-user or multitasking).

and how to pass that back to xterm
You don't. Xterm is a terminal, it provides access to the shell, nothing more. Similar to PuTTY on Windows or Terminal on OS-X. Xterm itself doesn't run anything, the OS does. I would suggest reading that shell tutorial and create a few simple shell scripts. Then it will become much clearer.
 
Maybe I am not describing this very well or using the right terms however...

#xterm -geometry 50x1 &

Creates a small window that stays on the screen. Yes, you can launch / execute other programs from it but it remains on the screen until you click 'X' or type in 'exit' - in other words is 'persists on the screen. Does that explain it better?

>user input and how to pass that back to xterm ... hmmm...

Again I wasn't describing this well and meant to say script, anyway the user type in a command, it appears in the box and when enter is pressed the run box disappears and the program is run.

Looking at other suggestions there's gmrun, fbrun, bashrun etc however I will look at the links given and try a few things myself :)
 
Creates a small window that stays on the screen. Yes, you can launch / execute other programs from it but it remains on the screen until you click 'X' or type in 'exit' - in other words is 'persists on the screen. Does that explain it better?
Yes, but it's just a process that runs until it exits. There is no persistence involved here, that's the wrong term for it. Persistence is something that would stay after it stopped running. The xterm process never stops running (only when you exit it), it's basically just looping forever waiting for input or output to handle. Any xterm process will be able to do exactly what that "run" window does. All you need to remember is that you need to background the process, or else the terminal is going to wait until that process is finished. Just open an Xterm, and run xterm. Notice how the prompt doesn't return on the first xterm? Now exit that second xterm and you'll see the prompt return on the first xterm. Compare this with running xterm &.
 
Maybe I am not describing this very well or using the right terms however...

#xterm -geometry 50x1 &

Creates a small window that stays on the screen. Yes, you can launch / execute other programs from it but it remains on the screen until you click 'X' or type in 'exit' - in other words is 'persists on the screen. Does that explain it better?

Time for a little more lesson. SirDice already explained: If you say "xterm" (with some arguments, but without & behind), it starts a process, which runs the xterm program. It will exit when it feels like it (for example it exits when you click the X, or when you say "exit" or enter control-D at the shell prompt). Sometimes it will exit if someone forces it to exit, like it getting forcibly stopped. For example, you could hit Control C in the terminal that started the xterm process. Let's not get into details of how exactly that works (there are signals involved, the mechanism of clicking on the X is a bit complex, and in reality the xterm process runs a sub-process which runs a shell).

Now, when you put an ampersand & at the end, what it tells the current shell is: start the xterm process, but then keep it running in the background. This means that it can continue to run, but many foreground operations (like starting another process, for example "ls", or hitting control C) do not affect it. You can always bring it back to the foreground (in most shells the command for that is "fg"), and hit Control C to stop it.

But: The xterm process is still a child process of the shell that started it, even when running in the background. Try this: from the shell, say "xterm &" (an xterm shows up), then "exit", and the xterm probably vanishes. That's because when a parent process exits, by default all the child processes also exit, background or not. This may be part of your problem with lack of "persistence".

There are ways around that. The xterm process itself could unhook itself from the parent; that's something called "daemonizing", and in BSD, there is a both a library function daemon(3) and a shell command daemon(8). That is pretty heavyweight. There is a simpler solution: Simply say "nohup xterm &". The nohup tells xterm to ignore when its parent goes away (the name comes this being implemented with the hangup signal called HUP), and the ampersand tells the shell to run in the background. This might get you closer.
 
I got it working.
The shell script, myscript.sh:
Code:
#! /bin/csh

set a="$<"
$a &

Then do:
chmod +x ~/myscript.sh
xterm -geometry 50x1 -e ~/myscript.sh
 
I didn't get very far lol but I ran from my window manager

"Run" f.exec "xterm -fg black -bg white -geometry 25x1 -T Run -e ~/run.sh &"

# Run Box
#!/bin/bash
read input
exec $input

Of course my downfall is that my run box stays on the screen until exited. And if I add & to the end of the last line of my run.sh of course it never runs the program within X

I'll admit its been years and also the other challenge was that is was all within a single script i.e. xterm was also run from within the shell script.
 
I'm trying to re-create something like fbrun or similar. Need to figure how to wait for input, then execute that input, then quit.
I've put together a couple of scripts for you, mainly to gain some experience with shell scripting features I've never used like trapped signals. Unless I misunderstood your requirements, they should mostly work for you. I say "mostly" because it doesn't inform you whether the command you entered was invalid or not; it just blindly tries to execute it, and if it doesn't work, then no big deal.

Personally, I would just use something like x11/rofi. x11/dmenu and x11/dmenu2 are also good choices if you want something that doesn't require so many dependencies. x11/bbrun is also possibly a good choice if you're in a Blackbox-like environment, though it seems a bit heavy in terms of dependencies. (jb_fvwm2 mentioned bbrun locks up Xorg, so I can't recommend that one.) However, any of them are likely more secure than the scripts below so long as they don't just pass the input line to a shell directly.

What I mean is that the run script below allows arbitrary shell code to be executed.
Code:
seq -f '%010.0f' 1073741824 | while read i; do echo $i; done >| /tmp/foo
will happily write all 11 GiB/Gio (approximately 11.8 GB/Go) to /tmp/foo). Any shell-based solution will almost certainly be just as much of a security risk. For this reason, I recommend keeping them somewhere inside your $HOME and ensure their permissions are set to 0700 (-rwx------) as I did so that only you can read, write, and execute them, especially if your $HOME has 0755 permissions (drwxr-xr-x) since that allows other users to read and execute files in your $HOME. At the very least, it should not be installed globally in some place like /usr/local/bin simply because if someone else can access your machine remotely, they can use this program maliciously.

Here's a script I called 'xrun', which acts as the "run box":
Code:
#!/bin/sh

# Adjust the path at the end if necessary.
xterm -fg black -bg white -geometry 25x1 -T Run -e ~/bin/run

The logic of the run script below works like this:
  1. Read a line of input as a command to execute.
  2. If the read fails, exit.
  3. If the line contains only spaces, blanks, etc.:
    • If the elif line is uncommented (see the end of this post), exit.
    • Otherwise, go to step 1.
  4. Otherwise execute the command.

And here's the corresponding run script (don't let the comments and whitespace fool you; it's only 17-18 lines of actual code, depending on whether you uncomment the elif line or not):
Code:
#!/bin/sh

# Start the process
#
# Once the shell has loaded and is executing, the kill
# command will send SIGUSR2, which will be trapped.  The
# trap will allow this script (and the xterm used to invoke
# it) to exit while leaving the shell executing the
# specified command.
#
# Params:
#   $1 PID of this script
#   $2 Command to execute
start() {
    trap 'exit 0' USR2
    nohup sh -c "kill -USR2 $1; $2" > /dev/null 2>&1 &
    wait
}

# Read input until EOF is received, an error occurs, or a
# non-empty string is read.
while true; do
    read -r input
    status="$?"

    # Remove trailing backslashes
    input="${input%%\\}"

    # 'read' returns 0 on success, 1 on EOF, or another
    # status if an error occurred or a trapped signal
    # interrupted the read.  This is true whether
    # anything was read or not.
    #
    # If exit status of 'read' wasn't 0 or 1, stop
    # trying to read input.
    expr "${status}"x : '[01]x' >/dev/null || break

    # Remove all spaces/blanks, tabs, carriage returns,
    # etc., resulting in an empty string if the input
    # contained only spaces.
    _input="$(echo "${input}" | tr -d '[:space:]')"

    # If the string wasn't empty, execute the command.
    # If the string was empty and EOF was received, stop
    # trying to read input.  Otherwise, clear the screen
    # to try again.
    if [ -n "${_input}" ]; then start $$ "${input}"
    elif [ x"${status}" = x1 ]; then break
    # Uncomment the 'elif' line below to allow exiting
    # with empty input
    #elif true; then break
    else tput up && tput ce
    fi
done

return "${status}"

All I did was place them in ~/bin and chmod 0700 ~/bin/run ~/bin/xrun. If you want to allow exiting when you press Enter/Return with no input (or only spaces, tabs, etc. as input), then there's a elif condition you can uncomment near the end.

Edit: Also checked the syntax with devel/hs-ShellCheck using shellcheck -o all -s SHELL ~/bin/run for all four shells supported by shellcheck and updated the run script accordingly. There are no more warnings now, and it even fixed a bug where backslashes would be ignored. This introduced a new problem with trailing slashes however, such as creating a file named \ using echo foo | tee foo \. It doesn't make sense to allow trailing slashes since only one line of input is read, so they are removed. Single-quoting the backslash will do the trick if you need to use a trailing backslash (or just rearrange the command line if possible).
 
Is it possible to test the syntax inside this script with the n flag ?
The scripts are syntactically valid:

Code:
$ if ! /bin/sh -n ~/bin/xrun
> then echo xrun FAIL
> else echo xrun OK
> fi ; if ! /bin/sh -n ~/bin/run
> then echo run FAIL
> else echo run OK
> fi
xrun OK
run OK
 
Back
Top