How to programatically paste to terminal without executing? - bash

Is it possible to type / paste something to the console without executing the command? Something that would emulate the normal Ctrl / Cmd + c, Ctrl / Cmd + v where the text is put on the current line without being executed so the user can continue typing, deleting, etc.
For example I have the following simple script:
#!/bin/bash
echo -n "foo" | pbcopy
pbpaste
Now when I run this, it just echoes foo% and goes on the next line.
Also, even if this would work, I would prefer a solution that works on both mac os and ubuntu (as far as I know pbcopy does not come pre-installed on all linux distros).
EDIT
Edit to explain the scenario better. Imagine the script above is called foo.sh. You run the script ./foo.sh and when it finishes you have a new prompt with only the text "foo"|. The | represents the cursor.

So... You already know about pbcopy and pbpaste in macOS. You probably want to know about xclip, which is a similar interface to X selections ("the clipboard") from the command line.
Each system (Aqua and X) handles clipboard data structures differently, and I'm not aware of any single tool which will function this way on both platforms. That said, you can perhaps write scripts that are portable between both systems:
#!/usr/bin/env bash
if type xclip >/dev/null; then
clip_copy="xclip"
clip_paste="xclip -o"
elif type pbcopy >/dev/null ; then
clip_copy="pbcopy"
clip_paste="pbpaste"
else
echo "ERROR: no clipboard functions. Where am I?" >&2
exit 1
fi
Also note that pbcopy/pbpaste support different data types, whereas xclip just deals with text.
That said, both of these functions deal with stdin and stdout. If you want to actually simulate keypresses (as your edit appears to imply), you need another tool.
In the Mac world, a number of options exist. Cliclick works well for me. This tool has full mouse support, but also has an option t:, which will simulate keyboard input. It seems reasonable that one might cliclick t:"$(pbpaste)", though I've never tried it.
You can also use AppleScript to print arbitrary text:
$ osascript -e 'tell application "System Events" to keystroke "Hello world."'
In X, xdotool seems to work.
$ xdotool type "Hello world."
To make a script which might run in both macOS and X environments, you could key on the output of uname -s:
#!/usr/bin/env bash
case "$(uname -s)" in
Darwin)
clip_copy="pbcopy" # note: $clip_copy isn't used in this script.
clip_paste="pbpaste"
type_cmd="osascript -e 'tell application \"System Events\" to keystroke \"%s\"'"
;;
*)
clip_copy="xclip"
clip_paste="xclip -o"
type_cmd='xdotool type "%s"'
;;
esac
text="$($clip_paste)"
printf "$type_cmd" "${text//[!A-Za-z0-9. ]/}" | sh
Note: untested. YMMV. May contain nuts.

Based on a gist in the comments I found a thread on stack exchange that helped me answer my own question: https://unix.stackexchange.com/a/213821.
I am only interested in supporting sh, bash, zsh so the following solution works fine:
#!/bin/bash
if [ "$(echo $ZSH_VERSION)" ]; then
print -z $#
else
# assume bash or sh
bind '"\e[0n": "'"$*"'"'; printf '\e[5n'
fi
Call with source inject.sh echo foo

Related

How to read user's input from bash (when catting a script) [duplicate]

I have a simple Bash script:
#!/usr/bin/env bash
read X
echo "X=$X"
When I execute it with ./myscript.sh it works. But when I execute it with cat myscript.sh | bash it actually puts echo "X=$X" into $X.
So this script prints Hello World executed with cat myscript.sh | bash:
#!/usr/bin/env bash
read X
hello world
echo "$X"
What's the benefit of executing a script with cat myscript.sh | bash? Why doesn't do it the same things as if I execute it with ./myscript.sh?
How can I avoid Bash to execute line by line but execute all lines after the STDIN reached the end?
Instead of just running
read X
...instead replace it with...
read X </dev/tty || {
X="some default because we can't read from the TTY here"
}
...if you want to read from the console. Of course, this only works if you have a /dev/tty, but if you wanted to do something robust, you wouldn't be piping from curl into a shell. :)
Another alternative, of course, is to pass in your value of X on the command line.
curl https://some.place/with-untrusted-code-only-idiots-will-run-without-reading \
| bash -s "value of X here"
...and refer to "$1" in your script when you want X.
(By the way, I sure hope you're at least using SSL for this, rather than advising people to run code they download over plain HTTP with no out-of-band validation step. Lots of people do it, sure, but that's making sites they download from -- like rvm.io -- big targets. Big, easy-to-man-in-the-middle-or-DNS-hijack targets).
When you cat a script to bash the code to execute is coming from standard input.
Where does read read from? That's right also standard input. This is why you can cat input to programs that take standard input (like sed, awk, etc.).
So you are not running "a script" per-se when you do this. You are running a series of input lines.
Where would you like read to read data from in this setup?
You can manually do that (if you can define such a place). Alternatively you can stop running your script like this.

Run a task in the background that waits until a certain word is entered in the terminal

I want to run a background set of commands from the terminal as a script or function that waits until a certain command is entered in the foreground. I don't want to wait for just a keypress, I need the word, string or character + enter. I've tried stuff like reading the last command in bash history but I am getting nowhere.
sh -c "until [ "$(history | tail -n 1 | awk '{print $2}')" = donut ];\
do : ; done; echo 'Why did you just type donut you doughnut? \
That is not even a word let alone a command.' &" &
sh -c "until [ "$(history | tail -n 1 | awk '{print $2}')" = ":" ]; do sleep 1 ; done; echo gotcha" &
Ideally I dont want any output like 'command not found' if the word is not a command and I don't want the word to execute if it is a command. That's why I tried the : command it is both and neither.
I also tried looking for ":\n" in various ways. I'm probably barking up the wrong tree here with the history command and suspect something to do with cat /dev/tty is required.
Any thoughts?
It depends on your operating system. For Mac, you need to create an executable file in your /usr/local/bin or /usr/bin. That is the directory where all the bash commands are executing from. You'll find things like "time", or "python", or "whoami" stored there. The actual executable can be anything, but you're trying to keep things simple, use a python script with a shebang. Then make the file into a unix executable. The actual program can be very simple, just force quit the process id of the "background process". I'm not sure how Windows organizes their "bin", but it's probably something similar.

Use pipe ("|") as first symbol in bash or zsh command

I have two simple scripts:
./cpthat
BlueM/cliclick types on the keyboard: Shift+Cmd+A, then Cmd+C, to the active iTerm terminal:
#!/bin/zsh
cliclick kd:shift,cmd t:a ku:shift t:c ku:cmd
pbpaste>$THATF
Shift+Cmd+A selects the output from the previous command, and
Cmd+C copies "that" to the clipboard.
pbpaste then writes that to the file $THATF defined system-wide.
./that
#!/bin/zsh
cat $THATF
This prints out the output of the last command as stored by cpthat.
(I know I can run $ command > $THATF directly but for other reasons I need to act retroactively on the command output. Also, not thread safe.)
The challenge:
I'm trying to get to where I can start a zsh or bash command with a pipe:
$ |grep -i sometext
Where, in effect, this happens:
$ that|grep -i sometext
Would this be possible somehow?
Overriding the pipe operator?
zsh config magic?
I'm using zsh heavily but am open for any solution.
You don't need to start with a |. Thegrep utility naturally reads STDIN.
Here's a contrived example:
# /bin/sh
# count_matches
grep $1 | wc -l
$ cat file | count_matches thing
You can see the | you're looking for is on the command line itself, not within the script
Similarly this works:
$ count_matches thing < file
In the first example, the STDIN is connected (via the pipe) to the output of the first command (trivially cat). In the second, it's from the actual file via redirection.
So, just get rid of the | and you should be good to go.
Animation showing that | alone at the beginning of command can be replaced automatically by the output of previous command:
Edit ~/.zshrc to override zsh's zle accept-line widget:
readonly THATF="path/to/your/temporary/file"
my-accept-line () {
if [[ "${BUFFER:0:1}" == '|' ]]; then
/usr/local/bin/cliclick kd:shift,cmd t:a ku:shift w:100 t:c ku:cmd
pbpaste>"${THATF}"
BUFFER='cat ${THATF} '${BUFFER}
fi
zle .accept-line
}
zle -N accept-line my-accept-line
Explanation
When you hit enter after entering a command, zsh runs the accept-line widget.
We override that widget, but before exiting we remember to call the original widget with zle .accept-line. With the dot prefix the factory widget is ran.
In iTerm2, shift+cmd+a selects all the output from the previous command, and cmd+c copies that to the system pasteboard.
We paste the contents of the pasteboard and redirects that to the temporary file declared earlier, pointed to by ${THATF}.
We prepend $BUFFER, the zsh special variable available within zle widget code, with the output of the previous command.
Dependencies, caveats:
This particular solution depends on:
cliclick dispatching macOS keyboard events. Perhaps a native solutions exist e.g. ANSI/escape sequence.
iTerm to handle the keybind for copying last commands output.
zsh for the zle widget.
Xode snippet above is proof-of-concept only and is wildly insecure.

Disable the arrow keys while a JAR is executed

The case is as follows:
1) I have a script somescript.sh.
2) At the end of the script, there is an eval statement which triggers the execution of a JAR.
3) When the JAR is executed, it will ask the user to provide an a/b/c option as a possible answer - important fact is that this questions come from the JAR application, so the console "logic" regarding the questions is written in Java and not placed in the script itself.
The problem: while executing the JAR program, the user can press the arrow keys and this will result in an ugly outcome such as ^[[A ^[[B ^[[C ^[[D.
This thread clarifies the ugly outcome: Why the terminal shows "^[[A" "^[[B" "^[[C" "^[[D" when pressing the arrow keys in Ubuntu?
Question: How is it possible to disable the arrow keys while the JAR is executed?
Now this is rather convoluted, but it`s still better than nothing.
The code below is a primitive BASH key filter to bypass cursor keys and halt any input on pressing Enter. Currently it lacks backspace support, but this is also doable if need be.
Requires BASH 4.2 or greater.
while true; do
read -s -N1 c1
read -s -N2 -t 0.001 c2
read -s -N1 -t 0.001 c3
case "$c1$c2$c3" in
$'\x0a' | $'') echo "";
break ;; # Enter
$'\x1b\x5b\x41') ;; # Up arrow
$'\x1b\x5b\x42') ;; # Down arrow
$'\x1b\x5b\x43') ;; # Right arrow
$'\x1b\x5b\x44') ;; # Left arrow
*) echo -n $c1$c2$c3 ;; # [guaranteed to be non-empty]
esac
done | tee >(stdbuf -o0 java -jar your_applet.jar)
read is a BASH built-in function that captures keyboard input.
echo is a BASH built-in function that prints what it`s told.
tee redirects the output to both STDOUT and the file provided.
>() is the so-called process substitution; it acts like a file for the writer and passes the resulting file`s contents as input to the command inside its brackets.
stdbuf disables output buffering, just in case.
Both tee and stdbuf are parts of coreutils, so they have to be present almost everywhere (maybe except Android, but that`s another story).
[UPD.] Last line, adapted:
done | tee >(stdbuf -o0 "$JAVA" $JAVA_OPTS -jar "$JBOSS_HOME/jboss-modules.jar" -mp "$JBOSS_MODULEPATH" org.jboss.as.domain-add-user "$#")
Hopefully that`d work.
[UPD2.] FINALLY! A different solution!
Just add this before eval:
stty=$(stty -g)
stty -ctlecho
trap "stty $stty" HUP INT QUIT PIPE TERM
Okay, this is also not perfect (the user is now able to travel the screen by pressing cursor keys…) but that`s still better than ^[[B^[[A^[[D^[[C

Reading input while also piping a script via stdin

I have a simple Bash script:
#!/usr/bin/env bash
read X
echo "X=$X"
When I execute it with ./myscript.sh it works. But when I execute it with cat myscript.sh | bash it actually puts echo "X=$X" into $X.
So this script prints Hello World executed with cat myscript.sh | bash:
#!/usr/bin/env bash
read X
hello world
echo "$X"
What's the benefit of executing a script with cat myscript.sh | bash? Why doesn't do it the same things as if I execute it with ./myscript.sh?
How can I avoid Bash to execute line by line but execute all lines after the STDIN reached the end?
Instead of just running
read X
...instead replace it with...
read X </dev/tty || {
X="some default because we can't read from the TTY here"
}
...if you want to read from the console. Of course, this only works if you have a /dev/tty, but if you wanted to do something robust, you wouldn't be piping from curl into a shell. :)
Another alternative, of course, is to pass in your value of X on the command line.
curl https://some.place/with-untrusted-code-only-idiots-will-run-without-reading \
| bash -s "value of X here"
...and refer to "$1" in your script when you want X.
(By the way, I sure hope you're at least using SSL for this, rather than advising people to run code they download over plain HTTP with no out-of-band validation step. Lots of people do it, sure, but that's making sites they download from -- like rvm.io -- big targets. Big, easy-to-man-in-the-middle-or-DNS-hijack targets).
When you cat a script to bash the code to execute is coming from standard input.
Where does read read from? That's right also standard input. This is why you can cat input to programs that take standard input (like sed, awk, etc.).
So you are not running "a script" per-se when you do this. You are running a series of input lines.
Where would you like read to read data from in this setup?
You can manually do that (if you can define such a place). Alternatively you can stop running your script like this.

Resources