make an ascii move in linux shell - linux

How can i make a train ascii to look like it's moving in the linux shell from right to left?
_-====-__-____-============-__
_( _)
OO( )_
0 (_ _)
o0 (_ _)
o `=-___-===-_____-========-__)
.o _________
. ______ ______________ | | _____
_()_||__|| ________ | | |_________| __||___||__
( | | | | | |Y_____00_| |_ _|
/-OO----OO**=*OO--OO*=*OO--------OO*=*OO-------OO*=*OO-------OO*=P

You could install the linux command 'sl' if you want trains running over your screen.
http://www.cyberciti.biz/tips/displays-animations-when-accidentally-you-type-sl-instead-of-ls.html

You should take a look at the ANSI Escape Codes, especially at moving the cursor and clearing the screen. For a quick start you can just clear \e[2J the entire screen and redraw everything.
Example:
#include <iostream>
using namespace std;
/* ASCII control character for ESCAPE (ESC) is "\e"
* Alternatives: Oct 033, Dez 27, Hex 1B
*
* The '\e' escape sequence is not part of ISO C and many other language
* specifications. However, it is understood by several compilers.
* The Escape character can also be entered by pressing the "Escape" or
"Esc"
* key on some systems.
*/
int main() {
cout << "\e[35m" << "Purple\n" << "\e[m";
// cout << "\e[2J\e[H" << "\e[35mPlease enter\e[m: \n";
cout << "foo" << '\x2B' << "\n";
return 0;
}
Or you use the library ncurses from GNU.

EDIT:
You can use pr -tro width, original solution with expand beneath :
Put your ascii image in a file (train.txt), but remove the first spaces (that all lines have in common).
i=0
while [ $i -lt 20 ]; do
clear
cat train.txt | pr -tro $i
sleep 1
(( i = i + 1 ))
done
Solution with expand:
Put your ascii image in a file (train.txt), but replace the first spaces (that all lines have in common) by 1 tab.
i=0
while [ $i -lt 20 ]; do
clear
cat train.txt | expand -$i
sleep 1
(( i = i + 1 ))
done
Alternative (oneliner image): use \r
Alternative for expand: use printf with a width in the formatstring.
Moving to the left: start with i=20 and use -gt 0 and (( i = i - 1 ))

Related

Basic Quadratic formula calculator shell script trouble

This is my very first shell script for a Unix class, this is one of the scripts I hope to submit for my final. However there are a few kinks I cannot seem to clear up, it seems to be arithmetic operation errors, and I can't seem to figure it out. Please be kind! thank you so much for your time.
lightgreen=`echo -en "\e[92m"
echo What are the values of a, b \& c?
LIGHTRED=`echo -en "\e[101m"
echo a value:
read a
echo b value:
read b
echo c value:
read c
discrim=$(($b**2 - 4*$a*$c))
sqrtd=$((sqrt($discrim) | bc ))
echo test $sqrtd
echo ${lightgreen}The discriminant is:${discrim}
#xone=$((( -$b + sqrt$discrim) / (2 * $a) | bc ))
#xtwo=$((( -$b - sqrt$discrim) / (2 * $a) | bc ))
xone=$((echo (-1*$b + sqrt($discrim)) / (2*$a) | bc ))
xtwo=$((echo (-1*$b - sqrt($discrim)) / (2*$a) | bc ))
echo ${lightgreen}The discriminant is:${discrim}
#if [$discrim -lt 0 ]
# echo $LIGHTRED There are no real solutions.
#
#
#
echo The two solutions are $xone $xtwo
I have tried to mess with the syntax a good amount, I'm not sure if it's the parentheses that mess me up or the sqrt function, I have tried to incorporate | bc but to no avail. Any help is greatly appreciated! :)
Don't hesitate to call man bash, man bc manual pages.
Use https://www.shellcheck.net/ to check your shell scripts.
Shellcheck also exists on command line and in Visual Studio Code with extension.
#! /usr/bin/env bash
# The first line is very important to now the name of the interpreter
# Always close " , ' , or ` sequences with same character
# Do not use old `...` syntax, replaced by $(...)
# Here, use $'...', to assign value with \... sequences
lightgreen=$'\e[92m'
lightred=$'\e[101m'
normal=$'\e[0m'
# It's better to put phrase between "..." or '...'
echo "What are the values of a, b & c?"
# Use read -p option to specify prompt
# Use read -r option to not act backslash as an escape character
read -p "a value: " -r a
read -p "b value: " -r b
read -p "c value: " -r c
# With bash only, it's only possible to use integer values
# discrim=$(($b**2 - 4*$a*$c))
# use bc instead
discrim=$(bc -l <<<"$b^2 - 4*$a*$c")
# The syntax:
# bc <<<"..."
# is equivalent to:
# echo "..." | bc
# but without pipe (|)
# Close the color change with normal return
echo "${lightgreen}The discriminant is: ${discrim}${normal}"
if [[ "${discrim:0:1}" == "-" ]]; then
echo "${lightred}There are no real solutions${normal}"
# ... complex ...
else
sqrtd=$(bc -l <<<"sqrt($discrim)")
echo "sqrt($discrim)=$sqrtd"
xone=$(bc -l <<<"(-1*$b + $sqrtd) / (2*$a)")
xtwo=$(bc -l <<<"(-1*$b - $sqrtd) / (2*$a)")
echo "The two solutions are: $xone and $xtwo"
fi

How to convert result as Integer in bash

when I do
$ ls | wc -l
703
It gave me the result 703, I want to print 702 (703-1)
How can I do it in bash?
You can use arithmetic expansion:
result=$(( $(ls | wc - l) - 1))
or just ignore one of the files
result=$(ls | tail -n+2 | wc -l)
Note that it doesn't work if filenames contain the newline character; use ls -q to get one filename per line in such a case. This applies to the first solution, too, if you're interested in the number of files and not the number of lines in their names.
(Cheeky answer) Remove one line from the output before counting :D
ls | sed '1d' | wc -l
How to convert result as Integer in bash
#choroba has already answered this question and it should have solved OP's problem. However, I want to add more to his answer.
The OP's wants to convert the result into Integer but Bash doesn't have any data type like Integer.
Unlike many other programming languages, Bash does not segregate its variables by "type." Essentially, Bash variables are character strings, but, depending on context, Bash permits arithmetic operations and comparisons on variables. The determining factor is whether the value of a variable contains only digits.
See this for arithmetic operation in Bash.
See this for a best example to learn the untyped nature of Bash. I have posted the example below:
#!/bin/bash
# int-or-string.sh
a=2334 # Integer.
let "a += 1"
echo "a = $a " # a = 2335
echo # Integer, still.
b=${a/23/BB} # Substitute "BB" for "23".
# This transforms $b into a string.
echo "b = $b" # b = BB35
declare -i b # Declaring it an integer doesn't help.
echo "b = $b" # b = BB35
let "b += 1" # BB35 + 1
echo "b = $b" # b = 1
echo # Bash sets the "integer value" of a string to 0.
c=BB34
echo "c = $c" # c = BB34
d=${c/BB/23} # Substitute "23" for "BB".
# This makes $d an integer.
echo "d = $d" # d = 2334
let "d += 1" # 2334 + 1
echo "d = $d" # d = 2335
echo
# What about null variables?
e='' # ... Or e="" ... Or e=
echo "e = $e" # e =
let "e += 1" # Arithmetic operations allowed on a null variable?
echo "e = $e" # e = 1
echo # Null variable transformed into an integer.
# What about undeclared variables?
echo "f = $f" # f =
let "f += 1" # Arithmetic operations allowed?
echo "f = $f" # f = 1
echo # Undeclared variable transformed into an integer.
#
# However ...
let "f /= $undecl_var" # Divide by zero?
# let: f /= : syntax error: operand expected (error token is " ")
# Syntax error! Variable $undecl_var is not set to zero here!
#
# But still ...
let "f /= 0"
# let: f /= 0: division by 0 (error token is "0")
# Expected behavior.
# Bash (usually) sets the "integer value" of null to zero
#+ when performing an arithmetic operation.
# But, don't try this at home, folks!
# It's undocumented and probably non-portable behavior.
# Conclusion: Variables in Bash are untyped,
#+ with all attendant consequences.
exit $?

How to print something to the right-most of the console in Linux shell script

Say I want to search for "ERROR" within a bunch of log files.
I want to print one line for every file that contains "ERROR".
In each line, I want to print the log file path on the left-most edge while the number of "ERROR" on the right-most edge.
I tried using:
printf "%-50s %d" $filePath $errorNumber
...but it's not perfect, since the black console can vary greatly, and the file path sometimes can be quite long.
Just for the pleasure of the eyes, but I am simply incapable of doing so.
Can anyone help me to solve this problem?
Using bash and printf:
printf "%-$(( COLUMNS - ${#errorNumber} ))s%s" \
"$filePath" "$errorNumber"
How it works:
$COLUMNS is the shell's terminal width.
printf does left alignment by putting a - after the %. So printf "%-25s%s\n" foo bar prints "foo", then 22 spaces, then "bar".
bash uses the # as a parameter length variable prefix, so if x=foo, then ${#x} is 3.
Fancy version, suppose the two variables are longer than will fit in one column; if so print them on as many lines as are needed:
printf "%-$(( COLUMNS * ( 1 + ( ${#filePath} + ${#errorNumber} ) / COLUMNS ) \
- ${#errorNumber} ))s%s" "$filePath" "$errorNumber"
Generalized to a function. Syntax is printfLR foo bar, or printfLR < file:
printfLR() { if [ "$1" ] ; then echo "$#" ; else cat ; fi |
while read l r ; do
printf "%-$(( ( 1 + ( ${#l} + ${#r} ) / COLUMNS ) \
* COLUMNS - ${#r} ))s%s" "$l" "$r"
done ; }
Test with:
# command line args
printfLR foo bar
# stdin
fortune | tr -s ' \t' '\n\n' | paste - - | printfLR

bc truncate floating point number

How do I truncate a floating point number using bc
e.g if I do
echo '4.2-1.3' | bc
which outputs 2.9 how I get it to truncate/use floor to get 2
Use / operator.
echo '(4.2-1.3) / 1' | bc
Dividing by 1 works ok if scale is 0 (eg, if you start bc with bc and don't change scale) but fails if scale is positive (eg, if you start bc with bc -l or increase scale). (See transcript below.) For a general solution, use a trunc function like the following:
define trunc(x) { auto s; s=scale; scale=0; x=x/1; scale=s; return x }
Transcript that illustrates how divide by 1 by itself fails in the bc -l case, but how trunc function works ok at truncating toward zero:
> bc -l
bc 1.06.95
[etc...]
for (x=-4; x<4; x+=l(2)) { print x,"\t",x/1,"\n"}
-4 -4.00000000000000000000
-3.30685281944005469059 -3.30685281944005469059
-2.61370563888010938118 -2.61370563888010938118
-1.92055845832016407177 -1.92055845832016407177
-1.22741127776021876236 -1.22741127776021876236
-.53426409720027345295 -.53426409720027345295
.15888308335967185646 .15888308335967185646
.85203026391961716587 .85203026391961716587
1.54517744447956247528 1.54517744447956247528
2.23832462503950778469 2.23832462503950778469
2.93147180559945309410 2.93147180559945309410
3.62461898615939840351 3.62461898615939840351
define trunc(x) { auto s; s=scale; scale=0; x=x/1; scale=s; return x }
for (x=-4; x<4; x+=l(2)) { print x,"\t",trunc(x),"\n"}
-4 -4
-3.30685281944005469059 -3
-2.61370563888010938118 -2
-1.92055845832016407177 -1
-1.22741127776021876236 -1
-.53426409720027345295 0
.15888308335967185646 0
.85203026391961716587 0
1.54517744447956247528 1
2.23832462503950778469 2
2.93147180559945309410 2
3.62461898615939840351 3
Try the following solution. It will truncate anything after the decimal point without a problem:
echo 'x = 4.2 - 1.3; scale = 0; x / 1' | bc -l
echo 'x = l(101) / l(10); scale = 0; x / 1' | bc -l
You can make the code a tad shorter by performing calculations directly on the numbers:
echo 'scale = 0; (4.2 - 1.3) / 1' | bc -l
echo 'scale = 0; (l(101) / l(10)) / 1' | bc -l
In general, you can use this function to get only the integer part of a number:
define int(x) {
auto s;
s = scale;
scale = 0;
x /= 1; /* This will have the effect of truncating x to its integer value */
scale = s;
return (x);
}
Save that code into a file (let's call it int.bc) and run the following command:
echo 'int(4.2 - 1.3);' | bc -l int.bc
The variable governing the amount of decimals on division is scale.
So, if scale is 0 (the default), dividing by 1 would truncate to 0 decimals:
$ echo '(4.2-1.3) / 1 ' | bc
2
In other operations, the number of decimals is calculated from the scale (number of decimals) of each operand. In add, subtract and multiplication, for example, the resulting scale is the biggest of both:
$ echo ' 4.2 - 1.33333333 ' | bc
2.86666667
$ echo ' 4.2 - 1.333333333333333333 ' | bc
2.866666666666666667
$ echo ' 4.2000 * 1.33 ' | bc
5.5860
Instead, in division, the number of decimals is strictly equal to th evalue of the variable scale:
$ echo 'scale=0;4/3;scale=3;4/3;scale=10;4/3' | bc
1
1.333
1.3333333333
As the value of scale has to be restored, it is better to define a function (GNU syntax):
$ echo ' define int(x){ os=scale;scale=0;x=x/1;scale=os;return(x) }
int( 4.2-1.3 )' | bc
2
Or in older POSIX language:
$ echo ' define i(x){
o=scale;scale=0;x=x/1;scale=o;return(x)
}
i( 4.2-1.3 )' | bc
2
You say:
truncate/use floor
And those are not the same thing in all cases. The other answers so far only show you how to truncate (i.e. "truncate towards zero" i.e. "discard the part after the decimal").
For negative numbers, the behavior is different.
To wit:
truncate(-2.5) = -2
floor(-2.5) = -3
So, here is a floor function for bc:
# Note: trunc(x) is defined as noted elsewhere in the other answers
define floor(x) {
auto t
t=trunc(x)
if (t>x) {
return t-1
} else {
return t
}
}
Aside:
You can put this, and other helper functions, in a file. For instance, I have this alias in my shell:
alias bc='bc -l ~/.bcinit'
And so whenever I run bc, I get all my utility functions from ~/.bcinit available by default.
Also, there is a good list of bc functions here: http://phodd.net/gnu-bc/code/funcs.bc
You may do something like this:
$ printf "%.2f\n" $(echo "(4530 / 4116 - 1) * 100" | bc -l)
10.06
Here I am trying to find the % change. Not purely bc though.

How to generate random numbers in the BusyBox shell

How can I generate random numbers using AShell (restricted bash)? I am using a BusyBox binary on the device which does not have od or $RANDOM. My device has /dev/urandom and /dev/random.
$RANDOM and od are optional features in BusyBox, I assume given your question that they aren't included in your binary. You mention in a comment that /dev/urandom is present, that's good, it means what you need to do is retrieve bytes from it in a usable form, and not the much more difficult problem of implementing a random number generator. Note that you should use /dev/urandom and not /dev/random, see Is a rand from /dev/urandom secure for a login key?.
If you have tr or sed, you can read bytes from /dev/urandom and discard any byte that isn't a desirable character. You'll also need a way to extract a fixed number of bytes from a stream: either head -c (requiring FEATURE_FANCY_HEAD to be enabled) or dd (requiring dd to be compiled in). The more bytes you discard, the slower this method will be. Still, generating random bytes is usually rather fast in comparison with forking and executing external binaries, so discarding a lot of them isn't going to hurt much. For example, the following snippet will produce a random number between 0 and 65535:
n=65536
while [ $n -ge 65536 ]; do
n=1$(</dev/urandom tr -dc 0-9 | dd bs=5 count=1 2>/dev/null)
n=$((n-100000))
done
Note that due to buffering, tr is going to process quite a few more bytes than what dd will end up keeping. BusyBox's tr reads a bufferful (at least 512 bytes) at a time, and flushes its output buffer whenever the input buffer is fully processed, so the command above will always read at least 512 bytes from /dev/urandom (and very rarely more since the expected take from 512 input bytes is 20 decimal digits).
If you need a unique printable string, just discard non-ASCII characters, and perhaps some annoying punctuation characters:
nonce=$(</dev/urandom tr -dc A-Za-z0-9-_ | head -c 22)
In this situation, I would seriously consider writing a small, dedicated C program. Here's one that reads four bytes and outputs the corresponding decimal number. It doesn't rely on any libc function other than the wrappers for the system calls read and write, so you can get a very small binary. Supporting a variable cap passed as a decimal integer on the command line is left as an exercise; it'll cost you hundreds of bytes of code (not something you need to worry about if your target is big enough to run Linux).
#include <stddef.h>
#include <unistd.h>
int main () {
int n;
unsigned long x = 0;
unsigned char buf[4];
char dec[11]; /* Must fit 256^sizeof(buf) in decimal plus one byte */
char *start = dec + sizeof(dec) - 1;
n = read(0, buf, sizeof(buf));
if (n < (int)sizeof(buf)) return 1;
for (n = 0; n < (int)sizeof(buf); n++) x = (x << 8 | buf[n]);
*start = '\n';
if (x == 0) *--start = '0';
else while (x != 0) {
--start;
*start = '0' + (x % 10);
x = x / 10;
}
while (n = write(1, start, dec + sizeof(dec) - start),
n > 0 && n < dec + sizeof(dec) - start) {
start += n;
}
return n < 0;
}
</dev/urandom sed 's/[^[:digit:]]\+//g' | head -c10
/dev/random or /dev/urandom are likely to be present.
Another option is to write a small C program that calls srand(), then rand().
I Tried Gilles' first snippet with BusyBox 1.22.1 and I have some patches, which didn't fit into a comment:
while [ $n -gt 65535 ]; do
n=$(</dev/urandom tr -dc 0-9 | dd bs=5 count=1 2>/dev/null | sed -e 's/^0\+//' )
done
The loop condition should check for greater than the maximum value, otherwise there will be 0 executions.
I silenced dd's stderr
Leading zeros removed, which could lead to surprises in contexts where interpreted as octal (e.g. $(( )))
Hexdump and dc are both available with busybox. Use /dev/urandom for mostly random or /dev/random for better random. Either of these options are better than $RANDOM and are both faster than looping looking for printable characters.
32-bit decimal random number:
CNT=4
RND=$(dc 10 o 0x$(hexdump -e '"%02x" '$CNT' ""' -n $CNT /dev/random) p)
24-bit hex random number:
CNT=3
RND=0x$(hexdump -e '"%02x" '$CNT' ""' -n $CNT /dev/random)
To get smaller numbers, change the format of the hexdump format string and the count of bytes that hexdump reads.
Trying escitalopram's solution didn't work on busybox v1.29.0 but inspired me doing a function.
sI did actually come up with a portable random number generation function that asks for the number of digits and should work fairly well (tested on Linux, WinNT10 bash, Busybox and msys2 so far).
# Get a random number on Windows BusyBox alike, also works on most Unixes
function PoorMansRandomGenerator {
local digits="${1}" # The number of digits of the number to generate
local minimum=1
local maximum
local n=0
if [ "$digits" == "" ]; then
digits=5
fi
# Minimum already has a digit
for n in $(seq 1 $((digits-1))); do
minimum=$minimum"0"
maximum=$maximum"9"
done
maximum=$maximum"9"
#n=0; while [ $n -lt $minimum ]; do n=$n$(dd if=/dev/urandom bs=100 count=1 2>/dev/null | tr -cd '0-9'); done; n=$(echo $n | sed -e 's/^0//')
# bs=19 since if real random strikes, having a 19 digits number is not supported
while [ $n -lt $minimum ] || [ $n -gt $maximum ]; do
if [ $n -lt $minimum ]; then
# Add numbers
n=$n$(dd if=/dev/urandom bs=19 count=1 2>/dev/null | tr -cd '0-9')
n=$(echo $n | sed -e 's/^0//')
if [ "$n" == "" ]; then
n=0
fi
elif [ $n -gt $maximum ]; then
n=$(echo $n | sed 's/.$//')
fi
done
echo $n
}
The following gives a number between 1000 and 9999
echo $(PoorMansRandomGenerator 4)
Improved the above reply to a more simpler version,that also runs really faster, still compatible with Busybox, Linux, msys and WinNT10 bash.
function PoorMansRandomGenerator {
local digits="${1}" # The number of digits to generate
local number
# Some read bytes can't be used, se we read twice the number of required bytes
dd if=/dev/urandom bs=$digits count=2 2> /dev/null | while read -r -n1 char; do
number=$number$(printf "%d" "'$char")
if [ ${#number} -ge $digits ]; then
echo ${number:0:$digits}
break;
fi
done
}
Use with
echo $(PoorMansRandomGenerator 5)
​​​​​​​​​​​​​

Resources