Given that two lines have been printed out in the terminal, is it possible to delete both of them so they may be replaced with two new lines?
I know you can use \r to replace 1 line (well, to move the cursor to the start of the line), but is there any way of doing this for the line above?
As an example, I'm running a program for computing the eigenfunctions of the Schrodinger equation and I want to keep an eye on how my variables are changing as it's being run, so I'd like an output like:
Param 1: xxxxxxx
Param 2: xxxxxxx
So I'd have the two parameters on two lines so they can be easily read and they'd be updated on each iteration of the program's matching function.
The cuu1 terminal capability allows you to go up a line. Pass it to tput in order to read the character sequence from the terminfo/termcap database, and then echo it twice.
echo -e '123\nabc\n'"$(tput cuu1)$(tput cuu1)"'*\n*'
You could also use $(tput cuu 2) instead of $(tput cuu1)$(tput cuu1)
-- Aesthir
Related
We write two similar scripts: one for bash (linux) and one for batch (dos/windows).
Even if the specific code is different we would like to visually compare (diff) both scripts and have the similar parts of code aligned side by side.
We use explicit comments with the same text to achieve this. But the beginning of the comments is different in both scripting (REM or :: in windows) and (# in linux).
Therefore there is a wrong alignment:
linux
windows
# first step
REM first step
foo.sh
foo.bat
# second step
REM second step
bar.sh
bar.bat
Is there a way to use a common character or sequence of characters to make the comments equal?
Is the use of : #; safe for both systems/scripts?
linux
windows
: #; first step
: #; first step
foo.sh
foo.bat
: #; second step
: #; second step
bar.sh
bar.bat
Are there any unwanted side effects?
: in bash is not exactly a comment. It is a void command.
A little bit like pass in some languages.
It helps, for example, to fill empty slots, if needed
if condition
then
:
else
doSomething
fi
So, you may use, somehow, as a sort of comment. That would works both in bash and batch (well, I know nothing of batch. But since you said that :: is a comment there). But beware that it is not exactly a comment. So there are some differences
For example
#!/bin/bash
echo one ||
:: foo
echo two
echo un ||
# bar
echo deux
Displays one, two and un but not deux.
Because echo one || prints one and then execute the following command only if it fails (which it doesn't). Here the following command is :: foo. Which is not executed (you wouldn't know, since it does nothing, but it is not executed). And the echo two is a brand new unrelated command that is executed.
Whereas, on the other hand, echo un || likewise prints un, and doesn't execute the next command, since echo un did not fail. But the next command here is echo deux. Because # bar doesn't count, since it is a comment.
And that is only one of the many examples one could probably find to show that : is not a comment.
But, well, if you use it being aware of that, I suppose you could use it to insert void comments in your bash code that would also be void in batch.
Edit:
I won't edit for each new example that comes to mind. But that one is pretty important
echo un # deux
echo one : two
prints
un
one : two
: is a command. So, as other commands, like ls not all occurrence of it is treated as so (no more than echo ls list the directory constant. ls is just a string here)
So, you can't use it as a replacement for inline comments. Only for full lines comments.
I have code :
printf '\r%s' 'first line here' 'second line here' 'thrid line heere'
sleep 1
printf '\r%s' 'iam new' 'new again' 'and new'
but the script show only one line
i want show three line, each line will update after one second, without new line again
only three line and update all three line
thanks
Assuming the objective is to print 3 lines, wait a bit, and then overwrite the same 3 lines, then you'll need to incorporate some additional code that provides for 'up and down' movement of the cursor.
While you can hardcode some of this based on a specific terminal, an easier (most of the time) approach is to use something like tput to manage cursor movement.
In this case you'll need the ability to not only 'go up' with the cursor but also overwrite/clear a previous set of output in the case where the new output is shorter in length; we'll also need to replace the current \r with \n.
The general approach:
EraseToEOL=$(tput el) # grab code for clearing from cursor to end of line; useful for erasing text from a previous longer print
tput sc # save the point we want to return to
printf "%s\n" 'first line here' 'second line here' 'third line here'
sleep 1
tput rc # return to our save point (tput sc), ie, the place where the cursor was located just before the 1st printf
printf "%s${EraseToEOL}\n" 'i am new' 'new again' 'and new'
The results:
FWIW, here's what it looks like if we remove ${EraseToEOL} from the 2nd printf:
tl;dr :
printf "One Line\nSecond line\nThird line"
sleep 1
printf "\033[1F\033[1Fnew 1\nnew 2\nnew 3"
Not sure what you are trying to do exactly.
But I am under the impression that you want to update three different lines.
Eg, you want your output to go from
first line here
second line here
third line heere (sic)
to
iam new
new again
and new
If so, you can't do that with \n, \r...
As Paul 's explained, those are inherited from the days when there was no screen but printers. And not modern printers: some electronically controlled good old typing machines.
So sending 'A' to the machine, types 'A'.
Etc. That the ascii code.
And some special codes meant special behaviour.
Such as '\r' which return to the beginning of the line (as it is possible with a typing machine, as you know if you've ever seen one). Or '\n', going to next line (using the right handle). Or '\07' ringing the bell. Etc.
But you cannot go back 1 line. Well, not with basic control char.
Now, depending on what terminal (the application emulating a physical terminal such as VT100, which itself is a device emulating a paperless printer. The "size of train is derived from size of roman horse ass" joke, is not entirely a myth) you are using, you may use special control sequences.
The most likely to work is ESC [ 1 F one.
So, in bash
printf "One Line\nSecond line\nThird line"
sleep 1
printf "\033[1F\033[1Fnew 1\nnew 2\nnew 3"
You may need to print some spaces to erase the remaining of the former line (or use some other control sequence to do so. You could check vt100 control sequences to find many others)
For example
printf "\n\n" # just to "create" the 2 extra lines
n=1
while true
do
printf "\033[1F\033[1F"
printf "n = $n\n"
printf "n² = $((n*n))\n"
printf "1000000/n = $((1000000/n)) " # <- note the trailing spaces because this line may become shorter and shorter
((n++))
sleep 1
done
(\033 is ESCape character. So each \033[1F sequence take you to the beginning of the previous line)
It is important tho to understand that you are assuming, doing so, that the terminal you are using understand those sequence. \n and \r are supposed to work with any device that understands ascii control char. VT100 Escape sequence are only working with devices that were designed to be compatible with those sequences. Which is the case of most terminal, but not all. A notable exception is jupyter notebook for example (And probably windows command terminal, but I don't have one to check).
So, if you need a clean solution for such things, you need to find a library such as bashsimplecurses.
That will adapt to any terminal.
Carriage returns (\r) ONLY reset to the beginning of the line.
Newline characters (\n) "roll the paper" a line vertically.
Imagine controlling a physical print head.
The result is that you can use \r for dynamic output effects like a countdown.
printf "\n" # get a clean line to start so you don't print over something
for c in {10..1}; do printf "\rNew lines in: %2.2s" $c; sleep 1; done # countdown in place
printf "\r%20.20s\r" "" # blank the line and reset to overwrite cleanly
printf "New line %2.2s!\n" {1..10}; # include \n's to move to next line, NOT overwrite
This counts down in place, doesn't leave the hanging 0 from the 10 (that's what the %2.2s if for), and writes the first of the lines that stay over where the countdown was without leaving hanging characters there (that's the %20.20s).
edit
I think I understand the OP a little better today.
The main point was to use printf '%s\n' instead of printf '\r%s'.
Others have done an excellent job of demonstrating how to move the cursor back up the screen to a previous point, but I wanted to throw one more perspective.
IF it's ok for this stuff to be the ONLY thing on the screen, there's a simple solution without quite so much explicit vertical cursor management -
clear # blank the screen, putting the cursor at the top
printf '%s\n' 'first line here' 'second line here' 'third line here'
sleep 1
clear # blank the screen and put the cursor at the top again
printf '%s\n' 'I am new' 'new again' 'and new'
Sometimes simple is easier to understand, use, and remember.
The other solutions here are a lot more flexible, though.
I recommend learning those.
This may be a more general question so sorry in advance. I am creating a script and thought it would be good to use multi-line strings instead of using multiple printf or echo statements. Say I have the following:
while :
do
printf "line 1
line 2
line 3"
done
The second and third lines would be printed with a space in front because of the indentation in the file.
l1
line 2
line 3
Is there a way to prevent that aside from removing the indentation on the code? Also, is it considered a better practice to just multiple printf/echo statements if you need to output information that spans multiple lines?
Indent with tabs (here whitespace) and use a heredoc (with <<-)
cat <<- EOF
line 1
line 2
line 3
EOF
Multi-line strings will always look a bit bad, or have some other downsides, I'm afraid. The most legible way to embed them in bash code is probably the here-doc, which shows the string (almost) exactly like it will look when output. As an extra knack, you can use extra punctuation to make the here-doc delimiter to stand out from the string itself too, like so:
if true
then
some commands
cat <<"____EndOfTextBlock____"
This text here
spans multiple
lines.
____EndOfTextBlock____
some other commands
even more commands
fi
Today, I wanted to test if filenames can contain commas and stumbled upon something else while opening cmd and trying these three tests:
echo a,b>a
This works as supposed (writes a,b to the file named a)
echo a>a,b
Does just the same! What happens here gets a bit clearer with the third test:
echo a>file,b this is a test
This will create a file named file containing a,b this is a test.
Now, three questions arise for me:
What is the explanation for this? If someone asked me, I would've guessed the comma separates commands or filenames, e.g. I would've expected the second test to create two files named a and b.
Is this behaviour documented somewhere?
Is it a cmd specific Windows extension or has it been like this since good old DOS times?
It's expected behaviour as ,;=<space><tab> are delimiters for parameters.
If you put the code into a batch file without echo OFF you will see
test.bat
echo a,b>a
echo a>a,b
echo a>file,b this is a test
Output
C:\temp>test.bat
C:\temp>echo a,b 1>a
C:\temp>echo a,b 1>a
C:\temp>echo a,b this is a test 1>file
After a redirection, only the next token is relevant, the rest is part of the normal line content.
It's unimportant where the redirection occurs in a line.
But there is the rule that when more than one redirection exists for the same stream, the last one will win.
> file.txt echo hello> nul world > con
This will result in hello world at the console.
Btw. There is still an obscure behaviour with redirection and lines extended by carets (multilines).
echo one two three^
four
Result: one two three four
But
echo one two >con three^
four
Result: one two four
The comma is a standard delimiter in batch as well as ; <space> = <tab> and everything after the comma is taken as another parameter to echo and only one parameter is taken for the redirection. You can try to enclose a,b in quotes and this should change the behaviour of the output and produce a,b file. You can also escape the delimiters with ^ - echo a>a^,b
You can try also echo a>a=b - it will be the same.
I have a file that has around million lines. I need to go to line number 320123 to check the data. How do I do that?
With n being the line number:
ng: Jump to line number n. Default is the start of the file.
nG: Jump to line number n. Default is the end of the file.
So to go to line number 320123, you would type 320123g.
Copy-pasted straight from Wikipedia.
To open at a specific line straight from the command line, use:
less +320123 filename
If you want to see the line numbers too:
less +320123 -N filename
You can also choose to display a specific line of the file at a specific line of the terminal, for when you need a few lines of context. For example, this will open the file with line 320123 on the 10th line of the terminal:
less +320123 -j 10 filename
You can use sed for this too -
sed -n '320123'p filename
This will print line number 320123.
If you want a range then you can do -
sed -n '320123,320150'p filename
If you want from a particular line to the very end then -
sed -n '320123,$'p filename
From within less (in Linux):
g and the line number to go forward
G and the line number to go backwards
Used alone, g and G will take you to the first and last line in a file respectively; used with a number they are both equivalent.
An example; you want to go to line 320123 of a file,
press 'g' and after the colon type in the number 320123
Additionally you can type '-N' inside less to activate / deactivate the line numbers. You can as a matter of fact pass any command line switches from inside the program, such as -j or -N.
NOTE: You can provide the line number in the command line to start less (less +number -N) which will be much faster than doing it from inside the program:
less +12345 -N /var/log/hugelogfile
This will open a file displaying the line numbers and starting at line 12345
Source: man 1 less and built-in help in less (less 418)
For editing this is possible in nano via +n from command line, e.g.,
nano +16 file.txt
To open file.txt to line 16.