Why does tmux not insert newlines when pasting a multi-line command? - bash

I often use a REPL-style coding method when writing shell scripts (or other relevant languages), and recently noticed the following issue. I run tmux so I can have my script open in vim in a pane side-by-side with a terminal prompt.
Tmux
If I try to paste multiple lines of commands at once using CMD-v on a Mac, i.e.
a=hello
b=World
echo $a $b
tmux does not process the newlines properly, but instead gives the following output:
[user#host: ~]$ a=hello
b='World'
echo $a $b
[user#host: ~]$ b='World'echo $a $b
If I clear the prompt, and run echo $a, I get hello echo'ed to the screen, but echo $b produces an empty line, and obviously the echo $a $b line does not get run.
I get the same output using a REPL like gnuplot, or when using rlwrap.
Alternate tmux attempt
The same issue occurs when using vim-slime, or using the relevant vim-slime system calls manually:
[user#host: ~]$ tmux set-buffer 'a=hello
> b=World
> echo $a $b
> '
[user#host: ~]$ tmux paste-buffer -p
a=hello
b=World
echo $a $b
[user#host: ~]$ a=hellob=Worldecho $a $b
I have tried tmux paste-buffer with, and without the -p flag for bracketed paste mode.
Plain bash shell, or GNU screen
If I perform the same CMD-v paste action in a normal bash shell (not in tmux), I get:
[user#host: ~]$ a=hello
[user#host: ~]$ b=World
[user#host: ~]$ echo $a $b
hello World
[user#host: ~]$
as expected. I get the same output when pasting in GNU screen (v4.04.00).
Question
Why does tmux not process the pasted commands line-by-line, as bash/gnu screen do? How do we fix this problem?
Already asked?
The same issue appears to have been asked at this stackoverflow question, and this other stackoverflow question, but not yet answered satisfactorily.
This answer offers a solution of a sleep line between each command, which does the trick, but it's a bit of a hack to assume how long each command will take to process before sending the next line of text. There must be a better way.
Versions
I am running Mac OS X El Capitan (v10.11.6), iTerm2 (v3.0.10), tmux (v2.2), GNU bash (v4.4.0).
The same results can be reproduced using Terminal.app (v2.6).

I solved the problem. I had been using reattach-to-user-namespace to interact with the OS X clipboard; however, according to the reattach-to-user-namespace github page:
Note: Under Yosemite (and later) pasteboard access seems to work fine
without the program from this repository.
I removed the set-option -g default-command "reattach-to-user-namespace -l bash" line from my .tmux.conf file. I also changed my tmux mapping to
bind -t vi-copy y copy-pipe "pbcopy"
and it copies text to the OS X clipboard from vi-copy mode as expected. Pasting text using the OS X default Cmd-v produces the expected behavior (like in screen or plain bash shell as described in the question). Thanks to #Alex Torok for prompting my config file debugging.

Related

How to evaluate a Julia expression from the terminal (not the REPL)?

Is there a way to run a (set of) Julia commands without entering the REPL?
E.g. julia.exe "using IJulia; notebook()" doesn't work.
MY end goal is to be able to create a clickable batch file that allows me and others I share it with to open Jupyter without needing to worry about the command line or REPL.
You can use the -e flag to the julia executable like this:
julia.exe -e "using IJulia; notebook()"
If you don't want the session to die after running, and you want it to give you a REPL afterwards, you can pass -i as:
julia.exe -e "using IJulia; notebook()" -i
This option and others is documented in the "Getting started" section of the documentation
Or by running the executable with the -h flag:
julia.exe -h
In addition with -e option, julia also read and evaluate stdin. Thus you can also do these using shell pipes/redirections:
$ echo '1+1' | julia
2
$ julia <<EOF
> 1+1
> EOF
2
$ julia <<< 1+1
2

bash read builtin does not echo input if script is piped to less

I stumbled upon this strange behavior of the bash builtin read.
I have a interactive script which has the potential of generating a large output. So naturally you append | less to it.
The script will still ask you for your input but it will not echo what you typed.
Here is a small sample.sh:
#!/bin/bash
echo "Type:"
read -r input
echo "Typed: ${input}"
sample.sh | less
I noticed that this is not a general issue with pipes (e.g. |cat works).
Any clue would be appreciated.
A SOLUTION which works for me:
#!/bin/bash
STTY_ORIG="$(stty -g)" # save stty settings
stty echo # enable echo
echo "Type:"
read -e -r input # use readline (backspace will not work otherwise)
echo "Typed: ${input}"
stty "${STTY_ORIG}" # restore stty settings
A SOLUTION which works for me and did not show an side effects.
Basically just tweak and restore the terminal settings...
#!/bin/bash
STTY_ORIG="$(stty -g)" # save stty settings
stty echo # enable echo
echo "Type:"
read -e -r input # use readline (backspace will not work otherwise)
echo "Typed: ${input}"
stty "${STTY_ORIG}" # restore stty settings
It actualy works for me.
The same script
martus#makus-pc:/tmp/src$ dpkg -l | grep bash
ii bash 4.4-5 amd64 GNU Bourne Again SHell
martus#makus-pc:/tmp/src$ uname -a
Linux makus-pc 4.9.0-4-amd64 #1 SMP Debian 4.9.65-3+deb9u1 (2017-12-23) x86_64 GNU/Linux
Edit: Does the script works without piping less? Less won't show anything typed untill you hit enter.

BASH get error code in parent terminal from child terminal?

In a bash script I start a new terminal with a command that gives an error. However I don't seem to be able to grab that error code:
#! /bin/bash
gnome-terminal -x bash -c "cat dksdamfasdlm"
echo $?
Output:
0
So I get the error code of the gnome-terminal command instead of the cat one. One suggestion I got, was to make a file with the code and read that from the parent bash. The problem is that I still seem to not be able and read the error code even that way:
#! /bin/bash
gnome-terminal -x bash -c "cat dksdamfasdlm; echo $?; sleep 2"
Output (on new terminal):
cat: dksdamfasdlm: No such file or directory
0
Why is that? Some suggestion on how to solve this? I just want to somehow grab the error in the new terminal from the parent bash.
It seems GNOME Terminal exits immediately after starting, which is obvious if you run for example gnome-terminal -x sleep 10. Since it doesn't wait for the command to finish, there's no way the return code will be that of the command. I could find no option in gnome-terminal --help-all to keep the process in the foreground.
Regarding your second question, you've double-quoted the command, so $? is expanded before running it. This should work:
gnome-terminal -x bash -c 'cat dksdamfasdlm; echo $?; sleep 2'
PS: The -x option is not documented in GNOME Terminal 3.8.4's gnome-terminal --help-all, various references don't help much, and there's no good explanation for why there's a -e option with identical semantics and different syntax.

bash echo not displaying values

When I run the following script, echo does not display anything and I don't know why. It works if I just type it into the terminal, but not from the shell script. Need some insight please. I might be tired but I'm very certain this should work:
#!/bin/sh
for n in `seq 1 10`
do
r=$RANDOM
t=$RANDOM
s=$RANDOM
f=$RANDOM
echo "$r $t $s $f"
done
echo "Done"
Your terminal probably runs a different shell than /bin/sh. For example, on Ubuntu, /bin/sh runs /bin/dash, but $RANDOM does not work there. You have to run /bin/bash or /bin/ksh to make it work.
When run from a terminal, you probably use bash, not sh.
Seems sh doesn't support $RANDOM and thus all variables you assign in your script will be assigned the empty string. Try changing the first line of your script to #!/bin/bash (or whereever bash is installed).

How do I get the command history in a screen session using Bash?

If I start a screen session with screen -dmS name, how would I access the command history of that screen session with a script?
Using the ↑, the last executed command appears, even in screen.
I use the default bash shell on my system and so might not work with other shells.
this is what I have in my ~/.screenrc file so that each new screen window gets its own command history:
Default Screen Windows With Own Command History
To open a set of default screen windows, each with their own command history file, you could add the following to the ~/.screenrc file:
screen -t "window 0" 0 bash -ic 'HISTFILE=~/.bash_history.${WINDOW} bash'
screen -t "window 1" 1 bash -ic 'HISTFILE=~/.bash_history.${WINDOW} bash'
screen -t "window 2" bash -ic 'HISTFILE=~/.bash_history.${WINDOW} bash'
Ensure New Windows Get Their Own Command History
The default screen settings mean that you create a new window using Ctrl+a c or Ctrl+a Ctrl+c. However, with just the above in your ~/.screenrc file, these will use the default ~/.bash_history file. To fix this, we will overwrite the key bindings for creating new windows. Add this to your ~/.screenrc file:
bind c screen bash -ic 'HISTFILE=~/.bash_history.${WINDOW} bash'
bind ^C screen bash -ic 'HISTFILE=~/.bash_history.${WINDOW} bash'
Now whenever you create a new screen window, it's actually launching a bash shell, setting the HISTFILE environmental variable to something that includes the current screen window's number ($WINDOW).
Command history files will be shared between screen sessions with the same window numbers.
Write Commands to $HISTFILE on Execution
As is normal bash behavior, the history is only written to the $HISTFILE file by upon exiting the shell/screen window. However, if you want commands to be written to the history files after the command is executed, and thus available immediately to other screen sessions with the same window number, you could add something like this to your ~/.bashrc file:
export PROMPT_COMMAND="history -a; history -c; history -r; ${PROMPT_COMMAND}"
screen doesn't maintain a history of the commands you type. Your shell may or may not keep a history. Since you appear to use bash, you can use the history command.
screen does appear to have a crude approximation of a history search (it merely searches the scrollback buffer for a command line. See the screen man page under the "history" command (bound to C-a { by default).
#technosaurus is right. $HISTFILE is written when bash exits, so you could exit one bash session, and start a new one, and the history should have been preserved through the file.
But I think there is a better way to solve your problem. The bash manual includes a description of the history built-in command. It allows you to save this history with history -w [filename] and read the history with history -r [filename]. If you don't provide a filename, it will use $HISTFILE.
So I would propose that you save the history inside your screen session to a specific file (or to your default $HISTFILE if you want). Then read the history file in the other bash session you want to access the history from. This way you don't have to exit the original bash session.
When you exit a terminal (or shell) the shell writes its history to $HISTFILE, so to get its history in another terminal you can type exit in the terminal you want the history of and it will get written.
cat $HISTFILE
#or tac, less, $EDITOR, ... depending on how you want to "access" it
use this:
screen -L
with capital L
it will store a copy of terminal input and output to a file named screenlog.0
or if you use -S to name it, the log file gets the screen name
I put the next lines into my .bashrc:
case "$TERM" in
screen)
declare SCREEN_NAME=$(echo $STY | sed -nr 's/[^.]*\.(.*)/\1/p')
if [[ $SCREEN_NAME ]]; then
HISTFILE="${HISTFILE}.${SCREEN_NAME}.${WINDOW}"
declare -p HISTFILE
fi
unset SCREEN_NAME
;;
*)
;;
esac
My default .bashrc has this 'case' basically with 'xterm*|rxvt*)' value, so I only added my 'screen' part into it. If you have not this 'case', you can use the next instead of it:
if [[ $TERM == screen ]]; then
declare SCREEN_NAME=$(echo $STY | sed -nr 's/[^.]*\.(.*)/\1/p')
if [[ $SCREEN_NAME ]]; then
HISTFILE="${HISTFILE}.${SCREEN_NAME}.${WINDOW}"
declare -p HISTFILE
fi
unset SCREEN_NAME
fi
And after I have an own bash_history for all window of my all screen.
Note: this not work in chroot!
history will show all the history command.

Resources