0

Is it possible for a running script to move itself into the background?

I have a goal: I run the script: ./script.sh. The script asks me for a password:

echo -n PASSWORD:
read -s pass
if [ "${scriptPassword}" == "$pass" ] && [[ ${pass_counter} = 0 ]]; then
pass_counter = $ ((pass_counter + 1))
echo "PASSWORD OK"

And then I want to achieve the same effect as when you press Ctrl+Z and then run the command bg.  Can I run such commands inside a script?

I want the script to start working as if it had been started with nohup ./script.sh &, but only after the user enters the correct password.

  • 4
    You probably mean bg %1. bg $1 does work, too, but for unexpected reasons... The reference to the newest job is %% so that should be used. – Hauke Laging Nov 11 '17 at 12:23
  • 1
    Do you want the bg to happen inside a script? What is your script doing? Please edit your question to improve it. I have not understood it as some other people. So please make your question more clear. – Basile Starynkevitch Nov 11 '17 at 12:53
  • 1
    Don't comment your own question, but edit your question to improve it. Your question is unclear and needs a lot of improvements. – Basile Starynkevitch Nov 11 '17 at 12:58
  • 4
    There are several typos in your if line. [ and ] need spaces around them. There must not be a space after $. Arithmetic comparisons are done with -eq not with =. Using [ and [[ together without the special features of [[ seems a bit strange. – Hauke Laging Nov 11 '17 at 13:29
  • 3
    Smells awfully bad as some XY problem. If the real issue is about database connection, that should be mentioned in the question (not only in comment), and the actual RDBMS should be mentioned, and the relevant commands also! As too often, complete lack of motivation in the question (not in comments) make them unclear. – Basile Starynkevitch Nov 11 '17 at 13:32
4

1. A Direct Approach

This is somewhat similar to Hauke Laging’s answer but simpler.  After your script has finished its interactive business, do

(kill -STOP $$; kill -CONT $$) &
sleep 1

Here, the script spawns a very small (asynchronous) subshell to STOP it (the script process), very much the same* as Ctrl+Z does, and then restart it with a contintue signal, very much the same as bg does.  You might not even need the sleep; I just thought that it might be safer if the script wasn’t doing anything important when the signal came in.

I have tested this, but only superficially, so there may be surprises if you use it in a real application context.
____________
* Different in that Ctrl+Z sends SIGTSTP.  You could probably use SIGTSTP in the script instead of SIGSTOP.  I tested it with SIGSTOP, and, if that works, I don’t see any reason to change it.

2. Arguably Simpler (and maybe more portable?)

Have the script invoke itself asynchronously.

if [ "$exported_pass" != "$scriptPassword" ]
then
    [ Insert here appropriate code to read pass from the user and loop     ]
    [ until it is correct (or there have been too many tries, so we exit). ]
    # So at this point, "$scriptPassword" == "$pass"
    exported_pass="$pass"
    export exported_pass
    nohup "$0" "$@" &
    exit
fi

The first time the script runs, $exported_pass will not be set, so it goes into the if-then and asks for the password.  Then it puts it into the exported_pass environment variable and invokes itself asynchronously — and then the main (parent) process exits.  The second invocation of the script, which is running in the background, sees the password in the environment, and just goes ahead and does its job.

  • Yeah... Your approach (2) is what I tried first but I forgot the exit and thus was under the wrong impression that this doesn't work. Unfortunately you managed to put three errors in a single line... pass_counter = $ ((pass_counter + 1)) – Hauke Laging Nov 11 '17 at 22:41
  • Well, I didn’t put them there; I copied them verbatim from the question. And, yes, it would have been better if I had fixed them (as I fixed the if itself), but I didn’t notice that I had overlooked those errors until six minutes after I posted the answer, and I didn’t want to do an edit for something so minor.   :-)   ⁠ – G-Man Nov 11 '17 at 22:47
  • Isn't the second solution an endless loop. When I miss type the password the subscript gets started anyway and it then starts itself again or am I missing something. Just read the code haven't tried it. – Raphael Ahrens Nov 12 '17 at 11:29
  • But [ "$exported_pass" != "$scriptPassword" ] is true when the second if condition is false, so it does it again, right? – Raphael Ahrens Nov 12 '17 at 11:35
  • 1
    @RaphaelAhrens: Check it again.  It’s incomplete, but I believe it explains better what’s supposed to be there. Thanks for calling it to my attention. – G-Man Nov 12 '17 at 11:53
2

You can set a trap in the parent shell before running the script so that the script can trigger the call of bg. The problem is that the trap is not executed while the script is running.

As the terminal just sends SIGSTOP to the script (after which the parent shell gains control again), the trick is for the script to do this itself:

# preparation in the parent shell
trap 'bg %%' USR1

The script:

#! /bin/bash

# parent shell has executed: trap 'bg %%' USR1

# pass or determine this value
PARENT_SHELL_PID=5646

read -p "enter value: " input
kill -USR1 $PARENT_SHELL_PID # execution of this in parent shell is delayed
kill -STOP $$ # returns control to parent shell

sleep 1000

sourcing a wrapper script

If it is acceptable to have two scripts and to source one of them (i.e. run it in the current shell context instead of as a subshell) then it becomes even easier:

The wrapper script:

PARENT_SHELL_PID=$$ ./script.sh
bg %%

The actual script:

#! /bin/bash

test -z "$PARENT_SHELL_PID" && exit 2

read -p "enter value: " input

# return control to sourced wrapper script
kill -STOP $$

# continue in the background
sleep 1000

Both are run with

. wrapper.sh
  • I would not like a parent shell. echo -n PASSWORD: read -s pass if ["$ {scriptPassword}" == "$ pass"] && [[$ {pass_counter} = 0]]; then pass_counter = $ ((pass_counter + 1)) echo "PASSWORD OK" It would be easier to run the second script already in the background nohup script2.sh & – MartinusP Nov 11 '17 at 13:14
  • @MartinusP: Please edit your question instead of adding comments. – Basile Starynkevitch Nov 11 '17 at 13:16
  • 1
    @MartinusP I do not understand your comment. You do not like the solution you asked for (any more)? Certainly there are different possible approaches. – Hauke Laging Nov 11 '17 at 13:24
  • I believe it’s possible to do better, and I have posted an answer. – G-Man Nov 11 '17 at 18:52
-2

Is it possible to move a running script to a background shell?

The use of "move" is confusing. You mean some other verb, very probably (I can't guess which one). I'm taking it literally....

A shell script is just a file (not the same as a process interpreting that script, and you could have several processes - or none - running that script). You could move it (with mv(1) or using rename(2) in some program) while it is running. But the shell interpreting it would have already some opened file descriptor on it, so that won't change anything. Actually, you don't want to move a script (and you don't move processes or jobs; however you can move files). The "move" verb is very confusing (at least to me, I am not a native English speaker) and I don't understand what and why you really want to do.

(a different question is what happens when you overwrite a script during its execution, but you don't ask that)

It looks like you want some program mimicking the action of a user inside a (pseudo-) terminal. Read the tty demystified page (terminals and pseudo-ttys are very complex, I forgot the details). Use expect to mimic a user inside a terminal.

BTW, managing passwords in scripts is risky (security holes are easy to make) and error prone, so smells really bad. I would try hard to avoid that, e.g. by using ssh with public & private keys.

So better redesign your entire thing to avoid supplying passwords automatically (inside scripts).

You apparently want to do (but it is unclear to me if you want to do that yourself in your terminal, or from inside another script):

CTRL+Z

 bg %%

This is about job control. The fact that what goes in the background is a process running some shell interpreting a script or some other plain executable program is irrelevant. Unix shells are ordinary programs from the kernel's point of view. IIRC, job control is related to process groups.

In a comment, you mention connecting to some database. Most RDBMS client programs provide some way to give the password in a more appropriate manner (perhaps --login-path for mysql command). If you want to do that from another script some here document could be handy (that here document could contain or synthesize some SQL commands). Notice also that RDBMS systems won't alter their databases when authentication failed.

Can I run such commands inside a script?

I don't understand what commands (is it bg %%) you want to run inside a script. If you want to drive job control from a script (but I believe you should not) then expect is probably more suitable.

I want the script to start working like nohup ./scripts.sh & but only after entering the correct password

That edit confuses me even more. What password are you talking about (I am not sure if your script provides a password to some RDBMS server, or requires one to be run completely)? I am guessing that you have some long-running script doing something related to databases (e.g. a backup lasting several hours). If that is indeed the case, you need to ask a different question. You could for example store the (interactively input) password somewhere and later run the backup in batch (perhaps using the batch command with some here document). BTW, various RDBMS have different ways to provide the password and do the authentication so you really need to mention, in your future question, what actual RDBMS you are using.

So consider improving your question to motivate it a lot and give more context. I'm only guessing, and I could guess very wrong!

Or maybe you want the equivalent of daemon(3) in your shell? That would make yet another question (and since bash is free software, you can always patch it to add a new builtin inside it). I think (but I am not sure at all) that exec shell builtin might be useful.

  • 2
    You have completely misunderstood the question. – Hauke Laging Nov 11 '17 at 12:50
  • Perhaps, then I am waiting for your better answer. – Basile Starynkevitch Nov 11 '17 at 12:50
  • 1
    Move a running script clearly refers to the process, not the file containing the script – Dani_l Nov 11 '17 at 12:55
  • AFAIK, you don't move processes neither. The OP is using the wrong words. – Basile Starynkevitch Nov 11 '17 at 12:56
  • 1
    The OP talks about CTRL+Z and you write "You apparently want to do CTRL+Z"? You obviously haven't even read the short question. Incredible for a 5k rep user. – Hauke Laging Nov 11 '17 at 13:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.