4

In a bash script, I want to output a command at the end, so the user only has to press Enter after my script has ended to execute this command.

E.g. the user needs to execute a command with certain parameters. Instead of typing it themself, my script outputs the command, with parameters to their command line, then exits. At this point my script is finished. The user sees a new prompt, with the command "pre-written" to it.

This gif should give an idea of what I'm talking about:

example

I saw this behaviour in several programs like hstr and qfc, and I think even the standard reverse-i-search does it.

I've searched Google for everything I could think of but I cannot find an answer.

7
  • Workaround: would it work for you something like to echo the command line you want to execute as the last statement of your script waiting for the interaction with the user that can press enter to confirm (then the script execute it and exit) or interrupt (CTRL-C) or modify/delete (it will be execute a new command or nothing)?
    – Hastur
    Oct 12, 2016 at 10:03
  • No, the command is just "source ~/.bashrc", which has to be done in the parent shell to have any use. Oct 13, 2016 at 13:57
  • Can you explain better? In the question you said you want to find a way so that your script executes some operations and creates the command with parameters for the user. Then you want to "pre-write" the command in the CLI so that the user has only to press enter to execute it. (Warning about re-sourcing ~/.bahsrc, that will be executed for each interactive bash shell). BTW the solution proposed (in the comment above and in the answer below) was to write the command in the a script readline, and to exit just after the interaction with the user. Without install any package...
    – Hastur
    Oct 13, 2016 at 14:24
  • ...if the command you want to create for the user is a source something you have not to execute the script but only source it (and even the source something will be sourced..).
    – Hastur
    Oct 13, 2016 at 14:25
  • The command is supposed to be pre-written in the cli, AFTER my script has exited. And the script itself cannot be sourced. Oct 13, 2016 at 14:35

6 Answers 6

4
+100

If you are using X, the xdotool tool might be a solution, by including in the script a command such as :

xdotool type command-line-to-display

If the new text becomes mixed with the script output, some acrobatics might be required.

3
  • 1
    Great, this is the only answer that does exactly what I want without any extra commands from the user. And I'm installing a bunch of stuff in the script already anways, so one more tool is ok. Oct 13, 2016 at 13:56
  • The only problem with this is that this command prints the command to stdout before the script ends, even if you reroute &> to /dev/null. But in the end, the right command is "pre-written" like I wanted it. Oct 13, 2016 at 14:33
  • There must be a way to stop it outputting to stdout. The source is available and can be studied and modified. The xdotool users mailing list is at [email protected] and might still be active although the website was last updated on 25 Apr 2015. The author's name is Jordan Sissel and his email is apparently jls at semicomplete.com.
    – harrymc
    Oct 13, 2016 at 15:12
3

Add these lines at the end of your bash script:

MY_COMMAND="ls"
MY_PARAMS=()
read -a MY_PARAMS -p $MY_COMMAND 
exec $MY_COMMAND ${MY_PARAMS[@]}

This assumes that the command you want to execute is ls, change it as it suits you. What you enter is stored as an array called MY_PARAMS, initialized by the first line; the command is then executed by repeating the command followed by the expansion of the array variable, which means all of its elements. The above is independent of how many elements you pass to your command. The exec shell command replaces the shell with the given command, effectively terminating your script.

EDIT:

If you want to add full command editing capabilities to your script, much beyond what read -e has to offer, you can do as follows: install rlwrap, then add the following code at the bottom of your Bash script:

stty -ixon
MYINPUT=()
HISTORY=$HOME/.bash_history
MYCOMMAND="ls"
MYINPUT=$(rlwrap -H $HISTORY -P $MYCOMMAND sh -c 'read REPLY && echo $REPLY')
stty ixon
exec sh -c "${MYINPUT[@]}"

rlwrap is a program which is capable of using all the features of readline, unlike the very poor Bash read -e option. It allows you to specify a file where to search for possible completions (I used the Bash history, $HOME/.bash_history, above, but you can write your own file). Also it can be configured (see the inputrc section in the readline manual) so you can choose between Emacs-style and vi-style editing, and allows you to search for matches forward (Ctrl+r) or backward (Ctrl+s) in the history file, edit the commands, and much much more.

I have added the stty -ixon/set ixon options because most terminal emulators intercept the control sequences Ctrl+r and Ctrl+s, and so on, and this disables this feature for the time being.

Also, the command you wish (I used ls for illustrative purposes) is pre-loaded, and can be executed as is (by hitting return) or modified via the readline capabilities of rlwrap.

What the above cannot do is to display a list of possible matches, allowing you to choose by means of your keypad. This requires some BASH programming (see dirkt's answer).

3
  • 1
    But that doesn't "pre-load" the suggestion into the parent shell executing the script, and you can't use the usual features of the shell like editing the line etc.
    – dirkt
    Oct 3, 2016 at 6:06
  • the core of his answer is that you treat the command like a normal string up to the point you actually execute it.
    – Sirex
    Oct 11, 2016 at 3:22
  • @dirkt Not true, there is a way to pre-load a given command (I used ls for illustration) in both versions. Oct 13, 2016 at 14:47
1

Not a full answer, but an explanation of what you've seen:

You can have a look at how qfc does it by inspecting qfc.sh. It uses special features of two shells: For zsh, the zle command, and for bash, the READLINE_LINE variable. Also, both variants use a function that is invoked within the shell, they don't just start a script and and make that information available on exit.

Doing it in a shell-independent way on exit of a script is an interesting problem. :-)

0

pause?

Command Prompt in Windows have the pause command which might do what you want to accomplish.

It outputs "Press any key to continue..." but its output can be avoided by pause > nul which redirects its own output to nowhere instead of onto the screen by default

1
  • Thanks, but not at all what I meant. I added an example for clarification. Also, Linux not Windows. Oct 2, 2016 at 16:44
0

Here is a short (, ugly?, buggy?) implementation with example using/for bash.

inline.sh:

inline () {
    stty_backup=$(stty -g)
    #I don't know why exactly, I just faced it
    stty sane
    res=$($READLINE_LINE 3>&1 >/dev/tty)
    stty $stty_backup
    READLINE_LINE=$res
    READLINE_POINT=${#READLINE_LINE}
}
#the bind is Alt-!, you can change it
bind -x '"\e!":"inline"'

hello.sh:

#!/bin/bash
echo Do you want:
echo 1 past?
echo 2 present?
echo 3 futur?

read -p "1 or 2 or 3? " r

if [ "$r" = "1" ]; then r="uptime"
elif [ "$r" = "2" ]; then r="date"
elif [ "$r" = "3" ]; then r="fortune fortunes"
else r=""
fi

echo $r >&3

Your script needs to be smart enough, the string you want to be returned at your prompt has to be returned through fd 3 (see inline.sh).

Now,

source inline.sh

type

./hello.sh

press Alt-!

You can source inline.sh in your bashrc, of course.

0

Workaround:

in the last line of your script you can write something like:

PrebuildCOMMAND="ls -lrt"             # just as example
echo "That's your command "           # keep your user informed
read -e -p " " -i "$PrebuildCOMMAND"  CMDtoEXECUTE
$CMDtoEXECUTE                         # here you execute what it was answered

Note: I notice just now that this is a variant (simplified) of what proposed by another answer (more complete).

You must log in to answer this question.

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