write a simple shell program to show a loader in TUI [duplicate] - shell

This question already has answers here:
How to add a progress bar to a shell script?
(41 answers)
Closed 7 years ago.
I am thinking of writing a simple shell program that will show an output in the console about the percentage work completed.
something like thiss:-
2%==>
which will increase the arrow with respect to the work done.
I donot want to print the loader every time in a new line.
what should be my approach?'
*I often see this thing is used in wget and similar commands
TIA

To update the line the cursor is on, send a CR ("carriage return", \r) to send ("return") the cursor to the beginning of the existing line, from which you can print new contents. Contrast this to the newline (\n), which moves the cursor to a new line.
To see this in action, try running the following:
printf '%s\r' "Existing contents being written here"
sleep 1
printf '%s\r' "New contents being written here "
sleep 1
printf '%s\n' "Writing final contents and moving to a new line"
printf '%s\n' "This is written to a second line."
Note how the second line has some extra whitespace on the end; this padding is there to make sure that the end of the original line's contents are overwritten.
That said, if you just want a status bar already built for you, there are numerous solutions for that already:
pv (Pipe Viewer) will do all the math for you, including with large streams, binary streams, etc.
The dialog program contains a "gauge" widget, with similar functionality. See this example of its usage on Unix StackExchange.

Related

Can interpolation be done on print output and in the middle of the output, is it possible to use gets in Ruby? (expecting reply from Ruby folks)

print """Input 1: (here I want *gets* to have some input)
Input 2:
"""
is it possible to get input in the middle of the printed 2 or more lines string in Ruby?
I can't tell exactly from your question, but I think this is what you are after:
This solution uses something called ANSI escape codes, which are "magic" sequences of characters you can use to control the terminal. You can do a variety of things with them, most commonly change output colour, but here we're using them to move the cursor around.
This works on my Mac, and should work on Linux - I'm not sure if this will work on Windows, you may have to enable ANSI escape sequences manually.
The general approach is:
Print the prompts
Move the cursor up to the line of the first prompt
Move the cursor forward to the end of that prompt
Accept input with gets
Move the cursor forward to the end of the second prompt
Accept input again
Here's a test script I wrote to do this, with enough comments to hopefully explain what is going on. I've changed the prompts from your original question so that they each have different lengths, to test that this solution can handle that.
prompt_1 = "First Input:"
prompt_2 = "Another Input:"
print "#{prompt_1}\n#{prompt_2}"
# Move the cursor back up to the line with the first prompt on
print "\033[1A"
# Move the cursor back to the start of the line, so we know we're in column zero
# Then move it to after the prompt
print "\033[1000D"
print "\033[#{prompt_1.length + 1}C"
# Get input
first_number = gets
# Hitting Enter after `gets` will have moved us to the next line already
# Move the cursor to the end of the second prompt
print "\033[1000D"
print "\033[#{prompt_2.length + 1}C"
# Get input again
second_number = gets
# Print output
puts "Addition is: #{first_number.strip.to_i + second_number.strip.to_i}"

Use bash to extract data between two regular expressions while keeping the formatting

but I have a question about a small piece of code using the awk command. I have not found an answer/solution anywhere.
I am trying to parse an output file and extract all data between the 1st expression (including) ATOMIC and 2nd expression (excluding) Bond. This data is to be sent to a new file $1_geom. So far I have the following:
`awk '/ATOMIC/{flag=1;next}/Bond lengths in Bohr/{flag=0}flag' $1` >> $1_geom
This script will extract the correct data for me, but there are 2 problems:
The line ATOMICis not extracted with the data
The data is extracted and appended to a single line. I want the data to retain the formatting from the parsed file (5 columns, variable amount of lines). Please see attachment to see a visual. Visual Example Attachment. Is there a different way to append data (other than >>) so that I can keep formatting?
Any help is appreciated, thank you.
The next is causing the first match to be skipped; take it out if you don't want that.
The backticks by themselves are a shell syntax error (unless your Awk script happens to produce valid shell commands). I'm guessing you have a useless echo or something like that in your actual script which disarms the error, but instead produces the symptoms you describe.
This was part of a code in a csh script and I did have an "echo" in front of this line. Removing the "echo" makes it work perfectly and addresses the 2 questions that I had.

Properly overwrite current line in terminal

In most terminals, if you haven't printed a newline character (or line feed; \n), printing a carriage return (\r) will reset your cursor to the beginning of the line so that subsequent characters overwrite what you've already output on the current line.
However, if you don't output enough characters to fully overwrite the previous contents of the line, the remaining characters will stay there. So, for example, the following pseudocode:
print "goodbye"
print "\rhello"
would result in helloye.
I'm wondering: is there any way to actually clear these remaining characters? I could simply keep track of them and then overwrite them with spaces, but that would, a) require me to keep track of them, and, b) still have trailing space characters, which isn't ideal, and I'd prefer not to do (I'm looking for a general solution that I can use whenever I come across this problem in the future). Any advice would be great; thanks!
Try using terminal escape
To clear from beginning of line to cursor: echo -e "\033[1K"
To clear line: echo -e "\033[2K"
Assuming you have VT100-compatible terminal or emulator
I used a leading carriage return a long time ago and it worked pretty well. I just tried it again on Linux Gnome Terminal program and it doesn't seem to work: nothing shows up on the screen. Changed it back to using a trailing line feed and every line I print gets displayed, but not overwritten. I suspect the lack of a line feed is what is keeping it from getting actually sent to the display.
See this about flushing.

How can I make my terminal prompt extend the width of the terminal?

I noticed in this video, that the terminal prompt extends the entire width of the terminal before breaking down to a new line. How can I set my PS1 variable to fill the remaining terminal space with some character, like the way this user did?
The issue is, I don't know how to update the PS1 variable per command. It seems to me, that the string value for PS1 is only read in once just as the .bashrc file is only read in once. Do I have to write some kind of hook after each command or something?
I should also point out, that the PS1 variable will be evaluated to a different length based on the escape characters that make up it. For example, \w print the path.
I know I can get the terminal width using $(COLUMNS), and the width of the current PS1 variable with ${#PS1}, do the math, and print the right amount of buffer characters, but how do I get it to update everytime. Is there a preferred way?
Let's suppose you want your prompt to look something like this:
left text----------------------------------------------------------right text
prompt$
This is pretty straight-forward provided that right text has a known size. (For example, it might be the current date and time.) What we do is to print the right number of dashes (or, for utf-8 terminals, the prettier \u2500), followed by right text, then a carriage return (\r, not a newline) and the left text, which will overwrite the dashes. The only tricky bit is "the right number of dashes", but we can use $(tput cols) to see how wide the terminal is, and fortunately bash will command-expand PS1. So, for example:
PS1='\[$(printf "%*s" $(($(tput cols)-20)) "" | sed "s/ /-/g") \d \t\r\u#\h:\w \]\n\$ '
Here, $(($(tput cols)-20)) is the width of the terminal minus 20, which is based on \d \t being exactly 20 characters wide (including the initial space).
PS1 does not understand utf-8 escapes (\uxxxx), and inserting the appropriate substitution into the sed command involves an annoying embedded quote issue, although it's possible. However, printf does understand utf-8 escapes, so it is easier to produce the sequence of dashes in a different way:
PS1='\[$(printf "\\u2500%.0s" $(seq 21 $(tput cols))) \d \t\r\u#\h:\w \]\n\$ '
Yet another way to do this involves turning off the terminal's autowrap, which is possible if you are using xterm or a terminal emulator which implements the same control codes (or the linux console itself). To disable autowrap, output the sequence ESC[?7l. To turn it back on, use ESC[?7h. With autowrap disabled, once output reaches the end of a line, the last character will just get overwritten with the next character instead of starting a new line. With this technique, it's not really necessary to compute the exact length of the dash sequence; we just need a string of dashes which is longer than any console will be wide, say the following:
DASHES="$(printf '\u2500%0.s' {1..1000})"
PS1='\[\e[?7l\u#\h:\w $DASHES \e[19D \d \t\e[?7h\]\n\$ '
Here, \e[19D is the terminal-emulator code for "move cursor backwards 19 characters". I could have used $(tput cub 19) instead. (There might be a tput parameter for turning autowrap on and off, but I don't know what it would be.)
The example in the video also involves inserting a right-aligned string in the actual command line. I don't know any clean way of doing this with bash; the console in the video is almost certainly using zsh with the RPROMPT feature. Of course, you can output right-aligned prompts in bash, using the same technique as above, but readline won't know anything about them, so as soon as you do something to edit the line, the right prompt will vanish.
Use PROMPT_COMMAND to reset the value of PS1 before each command.
PROMPT_COMMAND=set_prompt
set_prompt () {
PS1=...
}
Although some system script (or you yourself) may already use PROMPT_COMMAND for something, in which case you can simply add to it.
PROMPT_COMMAND="$PROMPT_COMMAND; set_prompt"

extending Bash tab completion: how to handle paths / partial commands? [duplicate]

This question already has an answer here:
Conditional trailing space with bash programmable completion
(1 answer)
Closed 5 years ago.
I've added tab completion for my program to bash. It works quite well, but I don't know how to handle partial completion of words.
Example: the user types
./program /home/user/De
and presses TAB. This is then completed to
./program /home/user/Desktop/
, but there's now a trailing whitespace after 'Desktop/', which is not what I want. Basically, I need a way of preventing bash from adding whitespace after the completed word.
I don't want to use bash's completion for paths.
Edit: This seems to be unclear. When TAB is pressed, my tab-completion function finds out which word to complete (in this case, that's '/home/user/De'). It then generates a list of matches (in this case, the only match is '/home/user/Desktop'). Bash uses this list to do the actual completion: It changes '/home/user/De' to '/home/user/Desktop/ '. I want to prevent bash from adding that whitespace after 'Desktop/'.
tried it in speech marks ?
./program "/home/user/[tab]"
am unsure about this question in total :) bash actually does the auto completion am unsure how you have written a script that auto completes something that bash does for u -
anyhow... you have stated you don't want bash to add extra space - unless you plan to rewrite bash or add to it - this won't be possible
Instead this is something u need to add to your own code to remove white spaces. try
var1=${var1//[[:space:]]}
after getting the value in where var1 is variable name for input

Resources