getting interactive, multiple line, formatted input from stdin in bash - bash

I want to be able to interactively get output from the terminal in a way similar to a hereDOC. Ie I want the user to be able to type multiple lines, then have that information passed into a file with all the formatting maintained. Something like this.
echo "Type your message below. To finish the letter type DONE by itself on a line"
file=mktmp
cat << DONE > $file
obviously this doesn't work, because the EOF is found before DONE. I thought about passing the user to something like VIM, but my less computer savy coworkers have a hard time with vim/emacs/nano.

You need to use an editor; standard input is just a stream of bytes, not an editor. However, you don't have to hard-code a specific editor. EDITOR is a standard environment variable meant to allow your script's caller to choose which editor is used.
: ${EDITOR:?Please set the environment variable EDITOR to the editor of your choice}
echo "Type your message below, then save and exit your editor."
"$EDITOR" "$file"
EDITOR is typically set by the user in their shell configuration file, but can be set on-demand when you run your script.
$ EDITOR=nano yourScript.sh

okay, so I came up with this, but please help me find something better or improve on it.
echo "Type your message below, to finish the letter press CTL+D"
mapfile message
file=`mktemp`
for x in `seq 0 ${#message[#]}`
do printf "${message[$x]}" >> $file
done
cat $file

Related

Is there a way to reproduce 'less' screen behavior in bash? [duplicate]

This question already has answers here:
Using the "alternate screen" in a bash script
(3 answers)
Closed 9 months ago.
I try to make a simple bash script that runs multiple tools, like dehader, cppcheck and a bunch of custom tools, and for readability, it cleans the terminal between each tool, and wait for the user to press the enter key before running the next tool.
clear
rm *.o
echo "removed object files"
rm __pycache__
echo "removed python cache files"
echo "everything cleaned, press enter to continue"
read a
clear
deheader
echo "deheader done, press enter to continue"
read a
clear
cppcheck
echo "cpp check done, press enter to exit"
read a
clear
Simple reproduction of what my script does
But I don't like this solution, because I want this script to execute in another screen (I don't know how to call it exactly), just like less does. This way, I could keep my terminal just like it was before the script call. Even if they aren't close to the less behavior, any suggestions that could help me are welcome.
I searched online what I could do to reproduce this behavior, but didn't find anything. I'm starting to doubt that's even possible.
Note: I don't want the 'scrolling' less behavior. I mean, there's no problem if I have to use it, but I don't particulary want it.
I think you want to run a new Terminal window and get back the prompt in your current Terminal window. If so, start a new Terminal that runs a command with lots of output and pipe it through less:
xterm -e "ls -lR / | less" &
The & runs the command in the background and gets you your prompt back in the current Terminal window.
Thanks to the comments, I figured it out.
From what I can understand, there are two ways to do it with the "cursor addressing mode":
By using tput smcup/rmcup
By entering/exiting the "cursor addressing mode" manually, when priting the escapes sequences related
This mode in terminal-implemented and is not supported by all terminals - and not always in the same way, for example multiple terminals don't reset the cursor position after entering this mode, so don't forget to clear your terminal - but is fortunately supported by a large majority of modern terminal emulators.
smcup/rmcup
Simply use the tput smcup command, and then, restore it using tput rmcup.
Example:
# save, clear screen
tput smcup
clear
# example "application" follows...
read -n1 -p "Press any key to continue..."
# example "application" ends here
# restore
tput rmcup
source
Escape sequence
You can enter this mode using printf \\33\[\?1047h, then leave it using printf \\33\[\?1047l.
Example:
# save, clear screen
printf \\33\[\?1047h
clear
# example "application" follows...
read -n1 -p "Press any key to continue..."
# example "application" ends here
# restore
printf \\33\[\?1047l
more informations
Try to avoid using both methods in the same code, or using one method to enter this mode and another to exit it, as it could lead to unexpected behavior.
Personnaly, I prefer the tcup method as it's more readable and supported by all Unix systems.

bash files that do like cat

I have an exercise where I need to do a file named my_cat.sh.
It has to work in the same way as the cat command.
Ex:
∼/C-DEV-110> echo Hello > test
∼/C-DEV-110> bash my_cat.sh test
Hello
I tried to search everywhere on the Internet but couldn't find any answers.
Copy your file my_cat.sh and modify it so it takes the file to show as its first parameter.
It's the sentences if it as any other way to find an answers.
(I'm new, so it may be really simple).
I tried to simply out a cat in the nano but i doesn't give back anything.
Thank you.
The cat command copies it's arguments, or the standard input if none was specified, to the standard output.
Now, if you had to implement it using cat, your script would look like this
#!/bin/sh
cat "$#"
However, that would be trivial. Now, if you had to implement without resorting to external commands, then you can use the shell's internal (built-in) read and echo commands combined with a loop to do the job. In order to read from a file instead of the standard input you'll need to use redirections.
It should look similar to this:
while read x
do
echo "$x"
done
Something like that would be the inner loop, there will be an other loop for iterating over the input files... for that you can either use "$1" and shift with a while loop, or a for loop over "$#".

Platypus shell script does not prompt for "read" command

I have a shell script like this :
#!/bin/sh
echo "What's your favorite color ?"
read user_color
echo "You like $user_color"
Whatever the settings I try with Platypus, the prompt is never shown on execution, thus the variable is not defined and never displayed. Is this something possible ?
read requires a controlling terminal (or at least a usable standard input), presumably in your environment no such terminal/input exists and so read cannot read any information (and echo has no standard output to send data to, assuming you don't see that either).
This limitation is in the platypus documentation and a workaround using CocoaDialog is given there as well.

In bash or shell, how does one input a line of text as a file without making a temp file?

Problem 1:
I'm using a self-made command, that for reasons, cannot be changed. The command requires a filename that it will read from like so: read-cmd "testtext.txt" I know it's possible to use files as a stream of input for some commands using input redirection, e.g. some-cmd < "text.txt", but I'm wondering if the opposite is true, whether I can use a line of text and make bash believe it's a file, so that I'd be able to do read-cmd "contents of what should be in a text file"
the only thing I've been able to do is
Example 1:
echo "contents of what should be in a text file" > tmptextfile
read-cmd "tmptextfile"
rm tmptextfile
I would however, really rather not do this, and rather just stream that line in as if it were a file. Is there any possible way to do this, or would it rely entirely on how read-cmd works?
Problem 2:
A very similar issue, however, instead of the file being an input to a command, it is the input to an option of a command. So, read-cmd2 -d "testtext.txt" ...
Example 2:
echo "contents of what should be in options text file" > tmpoptfile
read-cmd2 -d tmpoptfile ...
rm tmpoptfile
whether I can use a line of text and make bash believe it's a file,
Yes you can use process substitution for this:
read-cmd <(echo "contents of what should be in a text file")
Process substitution is a form of redirection where the input or output of a process (some sequence of commands) appear as a temporary file.

vim script leaves characters in stdin

I'm trying to use vim with -s option to run a script that replaces some lines in a file like this (text.txt):
test1
ab
ac
ae
test2
sd
Script file is like this (script):
:silent %s/test1\zs\_.\+\zetest2/\=substitute(submatch(0), '\n\(\w\)', '\n#\1', 'g')/g
:wq
It comments out lines between test1 and test2. Which is what I want. What I don't want though is output before and after prompt. I run it and get:
user#hostname: ~/vimtest$ vim -s script text.txt
^[[?1;2cuser#hostname: ~/vimtest$ 1;2c
So this ^[[?1;2c is bad news already but 1;2c is in the input as if I already typed it. If I hit enter it gives me a bash error. So I have to remove these symbols each time the script is used. Any ideas?
It seems like vim (or some vim startup script) is trying to figure out what type of terminal you are using. The ^[[?1;2c, with the last few characters left in the input buffer, is almost certainly part of your terminal emulator's response to a DA (Device Attributes) query. You can see this yourself by typing in bash:
printf '\033[c'
or, to see the complete return, pause a bit:
printf '\033[c'; sleep 0.1; echo
The response \033[?1;2c means "I'm a VT100 with Advanced Video Option.", which is what xterm and many other console programs respond. (The Linux console itself responds \033[?6c, which means "I'm a VT102.")
The reason that only 1;2c is left in the console input buffer, by the way, is that the initial escape code \033[? was ignored when it was read. The readline library will ignore it without echoing it, whereas normal console input will echo it and then ignore it; that's why the two shell commands above differ.
I can't reproduce this problem with my vim installation, so I don't really even know where to start looking. But you might try to see if disabling all startup files helps:
vim -u NONE -s script text.txt
If that helps, start disabling installed extensions one by one until you find the one which is causing the problem.
:%s/test1\zs\_.\+\ze\ntest2/\=substitute(submatch(0), '\n', '\n#', 'g')/g
:wq
this is tested here, it changed the input file in required way.
Some changes done based on your command:
add \n after \ze
in substitute() function we can just handle the \n, we don't need to capture the word after the \n
I noticed that you tagged the question with bash, so I thought a shell-solution should be accepted too.
awk '/test1/{p=1;print;next}/test2/{p=0;print;next}{$0=(p?"#":"")$0}7' file
this awk oneliner should do that for you. vim is very powerful editor, I love vim. But if you want to do some automatic transformation, I prefer a script or a proper text processing tool. On a linux box you can always find one. It is easier to test and debug.
Test with your input:
kent$ cat f
test1
ab
ac
ae
test2
sd
kent$ awk '/test1/{p=1;print;next}/test2/{p=0;print;next}{$0=(p?"#":"")$0}7' f
test1
#ab
#ac
#ae
test2
sd
If you want to save the text back to your file, you can :
awk '...' file > tmp.file && mv tmp.file file

Resources