Keep text in place regardless output bash printf - linux

This happens when numbers gets bigger...Just started with this mess.
Text being pushed around by bigger outputs...how do I contain this mess?
Accounts......: 5 Mail..........: 7
Banned........: 0 Pets..........: 1
Online........: 0 Tickets.......: 0
Guilds........: 1 Corpses.......: 0 PvP.......: 0
Members.......: 1 Characters....: 10 Gifts.....: 4 <-----HOWTO Reserve/Preserve spaces ?? ??
Should look like this:
Accounts......: 5 Mail..........: 7
Banned........: 0 Pets..........: 1
Online........: 0 Tickets.......: 0
Guilds........: 1 Corpses.......: 0 PvP.......: 0
Members.......: 1 Characters....: 10 Gifts.....: 4
Now this mess looks like this:
ch_count=$(mysql --defaults-extra-file="$sql_mycnf" -N --execute="SELECT count(*) FROM $db_characters.characters;"); &> /dev/null
cm_char="\033[0mCharacters\e[0;32m....:\033[0m $ch_count\e[31m"
line=" "
$cm_acco$line$cm_mail
$cm_bann$line$cm_pets
$cm_onli$line$cm_tick
$cm_guil$line$cm_corp$line$cm_pvps
$cm_memb$line$cm_char$line$cm_gifts
On another server there is same outputs but because they are smaller it looks fine:
Accounts......: 4 Mail..........: 0
Banned........: 0 Pets..........: 0
Online........: 0 Tickets.......: 0
Guilds........: 0 Corpses.......: 0 PvP.......: 0
Members.......: 0 Characters....: 2 Gifts.....: 0
Edit this line to make it work?
Is this correct place to begin?
cm_char="\033[0mCharacters\e[0;32m....:\033[0m $ch_count\e[31m"
Kill me.

Make 2 functions that will format the fields and use them:
dot_field() {
# todo Change implementation when field can be 2 words with a space in between
printf "%-14.14s:" "$1" | tr ' ' '.'
}
space_number() {
printf "%-7.7s" "$1"
}
printline() {
# Todo: add logic when only 4 parameters are given
echo " $(dot_field $1) $(space_number $2)$(dot_field $3) $(space_number $4)$(dot_field $5) $(space_number $6)"
}
printline "Guilds" 1 "Corpses" 0 "PvP" 0
printline "Members" 1 "Characters" 10 "Gifts" 4
printline "LongFieldName" 1 "High" 999999 "X" 2
EDIT: Adding colors.
You don't want to have your code full of escape codes for the colors. It depends on the full context how you would like to structure your color codes, I give an example for the limited context of the question. It should give you an idea how you can make something like this for yourself.
init_colors() {
reset=$(tput sgr0)
bold=$(tput bold)
black=$(tput setaf 0)
red=$(tput setaf 1)
green=$(tput setaf 2)
yellow=$(tput setaf 3)
blue=$(tput setaf 4)
magenta=$(tput setaf 5)
cyan=$(tput setaf 6)
white=$(tput setaf 7)
user_color=$bold
}
# colorstring reads from stdin and uses parameter 1 as an escape sequence
# with more parameters the first is used as a color, the other as the string to be modified
# It will set colors until the last space sequence
colorstring() {
case $# in
0) # invalid
echo "colorstring called without parameters"
;;
1)
sed -r "s/^.*[^ ]/$1&${reset}/"
;;
*)
color="$1"
shift
sed -r "s/^.*[^ ]/${color}&${reset}/" <<< "$#"
;;
esac
}
dot_field() {
# todo Change implementation when field can be 2 words with a space in between
printf "%-14.14s" "$1" | colorstring ${cyan} | tr ' ' '.'
# The : must be printed in a second statement when you don't want cyan dots.
printf ':'
}
space_number() {
printf "%-7.7s" "$1" | colorstring ${red}
}
printline() {
echo " $(dot_field $1) $(space_number $2)$(dot_field $3) $(space_number $4)$(dot_field $5) $(space_number $6)"
}
# init the color variables
init_colors
# Next echo not needed, just testing the new colorstring function
echo "$(colorstring ${blue} blue string) $(colorstring ${red} red car) $(colorstring ${white} white snow) $(colorstring ${yellow} yellow marker) $(colorstring ${cyan} cyan) "
printline "Guilds" 1 "Corpses" 0 "PvP" 0
printline "Members" 1 "Characters" 10 "Gifts" 4
printline "LongFieldName" 1 "High" 999999 "X" 2

As you may alredy know there are some spesial system sequinces
that controll output to terminal. This for example will turn text red '\e[31m'
and this will print text in certain column\line '\e[${LINE};${COLUMN}H'.
So we will use that. First i will create this 'data' array with "name value" pairs
to simulate your case.
data=(
"Accounts 5"
"Banned 10"
"Online 40"
"Guilds 4"
"Members 1"
"Mail 71"
"Pets 43"
"Tickets 0"
"Corpses 101"
"Characters 10"
"PvP 0"
"Gifts 4"
)
I'm using this table when working with text output so lets used it to
#--------------------------------------------------------------------+
#Color picker, usage: printf ${BLD}${CUR}${RED}${BBLU}"Hello!)"${DEF}|
#-------------------------+--------------------------------+---------+
# Text color | Background color | |
#-----------+-------------+--------------+-----------------+ |
# Base color|Lighter shade| Base color | Lighter shade | |
#-----------+-------------+--------------+-----------------+ |
BLK='\e[30m'; blk='\e[90m'; BBLK='\e[40m'; bblk='\e[100m' #| Black |
RED='\e[31m'; red='\e[91m'; BRED='\e[41m'; bred='\e[101m' #| Red |
GRN='\e[32m'; grn='\e[92m'; BGRN='\e[42m'; bgrn='\e[102m' #| Green |
YLW='\e[33m'; ylw='\e[93m'; BYLW='\e[43m'; bylw='\e[103m' #| Yellow |
BLU='\e[34m'; blu='\e[94m'; BBLU='\e[44m'; bblu='\e[104m' #| Blue |
MGN='\e[35m'; mgn='\e[95m'; BMGN='\e[45m'; bmgn='\e[105m' #| Magenta |
CYN='\e[36m'; cyn='\e[96m'; BCYN='\e[46m'; bcyn='\e[106m' #| Cyan |
WHT='\e[37m'; wht='\e[97m'; BWHT='\e[47m'; bwht='\e[107m' #| White |
#----------------------------------------------------------+---------+
# Effects |
#--------------------------------------------------------------------+
DEF='\e[0m' #Default color and effects |
BLD='\e[1m' #Bold\brighter |
DIM='\e[2m' #Dim\darker |
CUR='\e[3m' #Italic font |
UND='\e[4m' #Underline |
INV='\e[7m' #Inverted |
COF='\e[?25l' #Cursor Off |
CON='\e[?25h' #Cursor On |
#--------------------------------------------------------------------+
# Text positioning, usage: XY 10 10 "Hello World!" |
XY () { printf "\e[${2};${1}H${3}"; } # |
#--------------------------------------------------------------------+
# Print line, usage: line - 10 | line -= 20 | line "Hello World!" 20 |
line () { printf -v LINE "%$2s"; printf -- "${LINE// /$1}"; } # |
# Create sequence like {0..X} |
cnt () { printf -v _N %$1s; _N=(${_N// / 1}); printf "${!_N[*]}"; } #|
#--------------------------------------------------------------------+
There are all basic colors set as vars to easily insert in text and some
usefull functions like XY i'll use it to print text in serrtain position.
Lets set some vars
space_betwen=7 # space betwen columns
X=$space_betwen # starting X(column) position
Y=10 # starting Y(line) position
dot_string='...............: ' # dot string to simulate your output
dot_length=${#dot_string} # this will calculate the length of the dot string
Ok we are ready to go but first lets clear all text from terminal screen
clear
And now we can iterate through data and print text in 3 columns by 4 lines
for item in "${data[#]}"; {
((counter++)) # lets count items to know when start next column
read name value <<< $item # get naame and value from current item
XY $X $Y "$dot_string$RED$value$DEF" # print dot string and red value
XY $X $Y "$YLW$name$DEF" # name will be printed ower dots in yelow color
((Y++)) # go to next line by increasing Y value
# chek if we print 4 lines than set Y to start poosition and inc X to space_betwen+dot_length
((counter%4)) || { Y=10; ((X+=space_betwen+dot_length)); }
}
The final script will be like this
#!/bin/bash
data=(
"Accounts 5"
"Banned 10"
"Online 40"
"Guilds 4"
"Members 1"
"Mail 71"
"Pets 43"
"Tickets 0"
"Corpses 101"
"Characters 10"
"PvP 0"
"Gifts 4"
)
. ~/SCR/color # include color table
space_betwen=7 # space betwen columns
X=$space_betwen # starting X(column) position
Y=10 # starting Y(line) position
dot_string='...............: ' # dot string to simulate your output
dot_length=${#dot_string} # this will calculate the length of the dot string
clear
for item in "${data[#]}"; {
((counter++)) # lets count items to know when start next column
read name value <<< $item # get naame and value from current item
XY $X $Y "$dot_string$RED$value$DEF" # print dot string and red value
XY $X $Y "$YLW$name$DEF" # name will be printed ower dots in yelow color
((Y++)) # go to next line by increasing Y value
# chek if we print 4 lines than set Y to start poosition and inc X to space_betwen+dot_length
((counter%4)) || { Y=10; ((X+=space_betwen+dot_length)); }
}
XY 1 20 "$DEF" # one more time to move cursor down in the end
And the output will be like this

Related

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 $?

Gnuplot 4d map colorbar with different transparency based on data

I am trying to plot 4d plot using splot (x y z value). I would like to have 4th column shown as heat color. Up to this point, I am fine. What I can't figure out, after searching answers online, is to have the color of the dots being transparent but with different transparency based on their value.
For example, let's say I have the following data set:
0 0 0 0.1
0 0 1 0.2
0 1 0 0.2
0 1 1 2
1 0 0 1
1 0 1 3
1 1 0 0.5
1 1 1 4
Now, I want to make the colorbar (for the 4th column) to be as such: the closer the 4th column value is to 1, the more transparent the dot/point in the figure it will be. All the places I've look can only give me uniform transparency for the entire colorbar.
I wonder if anyone has dealt with this before, or has an idea how to do this.
Thank you very much!
I did not fully understand how you want the transparency to depend on the value, so I will give a general answer, where you can substitute the transparency function for your own.
While you can specify transparency for line colors, this appears to not be possible when using palette, which would be the most straightforward way to achieve what you want. The furthest you can go with only using gnuplot is to make the colors appear transparent, as in the following script, where in.data is a file with your sample data.
#!/usr/bin/env gnuplot
set term pngcairo
in_data = "in.data"
set out "out.png"
# function to combine color and alpha channels with white background
# 0: no transparency, 1: fully transparent
make_transparent(x1, t) = (1-t)*x1 + t
# a function to decide transparency
# the input and output must be in range of [0,1]
#get_transparency(x1) = 0 # no transparency
#get_transparency(x1) = 1 # fully transparent
get_transparency(x1) = 1 - x1 # smaller values are more transparent
# convenience function to truncate values
minval(x1, x2) = x1<x2?x1:x2
maxval(x1, x2) = x1>x2?x1:x2
truncval(x1, xmin, xmax) = maxval(minval(x1, xmax), xmin)
trunc(x1) = truncval(x1, 0, 1)
# the default palette consists of rgbfunctions 7,5,15
# we redefine their transparency enabled versions here
# the input and output must be in range of [0,1]
# see other formulae with "show palette rgbformulae" command in gnuplot
f7(x1) = make_transparent(sqrt(x1) , get_transparency(x1))
f5(x1) = make_transparent(x1**3 , get_transparency(x1))
f15(x1) = make_transparent(trunc(sin(2*pi*x1)), get_transparency(x1))
set palette model RGB functions f7(gray),f5(gray),f15(gray)
splot in_data palette
This script assumes that the background is white, but can be adapted to any other solid background. It falls apart once the points start overlapping however.
To get real transparency, you would need to plot each data point as a separate line and give it a distinct line color. This could be achieved by pre-processing the data, as in the following bash script.
#!/usr/bin/env bash
set -eu
in_data="in.data"
out_png="out.png"
pi=3.141592653589793
# function to convert data value into rgba value
function value2rgba()
{
# arguments to function
local val="${1}"
local min="${2}"
local max="${3}"
# normalized value
local nval=$( bc -l <<< "(${val}-${min})/(${max}-${min})" )
#### alpha channel value ####
local alpha="$( bc -l <<< "255 * (1-${nval})" )"
# round to decimal
alpha=$( printf "%0.f" "${alpha}" )
#### red channel value ####
# rgbformulae 7 in gnuplot
local red="$( bc -l <<< "255 * sqrt(${nval})" )"
# round to decimal
red=$( printf "%0.f" "${red}" )
#### green channel value ####
# rgbformulae 5 in gnuplot
local red="$( bc -l <<< "255 * sqrt(${nval})" )"
local green="$( bc -l <<< "255 * ${nval}^3" )"
# round to decimal
green=$( printf "%0.f" "${green}" )
#### blue channel value ####
# rgbformulae 15 in gnuplot
local blue="$( bc -l <<< "255 * s(2*${pi}*${nval})" )"
# round to decimal
blue=$( printf "%0.f" "${blue}" )
# make sure blue is positive
if (( blue < 0 ))
then
blue=0
fi
### whole rgba value
local rgba="#"
rgba+="$( printf "%02x" "${alpha}" )"
rgba+="$( printf "%02x" "${red}" )"
rgba+="$( printf "%02x" "${green}" )"
rgba+="$( printf "%02x" "${blue}" )"
echo "${rgba}"
}
# data without blank lines
data="$( sed -E "/^[[:space:]]*$/d" "${in_data}" )"
# number of lines
nline=$( wc -l <<< "${data}" )
# get the minimum and maximum value of the 4-th column
min_max=( $( awk '{ print $4 }' <<< "${data}" | sort -g | sed -n '1p;$p' ) )
# array of colors for each point
colors=()
while read -r line
do
colors+=( $( value2rgba "${line}" "${min_max[#]}" ) )
done < <( awk '{ print $4 }' <<< "${data}" )
# gather coordinates into one row
coords=( $( awk '{ print $1,$2,$3 }' <<< "${data}" ) )
gnuplot << EOF
set term pngcairo
set out "${out_png}"
\$DATA << EOD
${coords[#]}
EOD
nline=${nline}
colors="${colors[#]}"
unset key
splot for [i=0:nline-1] \$DATA \
u (column(3*i+1)):(column(3*i+2)):(column(3*i+3)) \
pt 1 lc rgb word(colors, i+1)
EOF
These scripts were tested with gnuplot 5.

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

Bash, print 0 in terminal each time a non recognised argument is input

I have a bash program which extracts marks from a file that looks like this:
Jack ex1=5 ex2=3 quiz1=9 quiz2=10 exam=50
I want the code to execute such that when I input into terminal:
./program -ex1 -ex2 -ex3
Jack does not have an ex3 in his data, so an output of 0 will be returned:
Jack 5 3 0
how do I code my program to output 0 for each unrecognized argument?
If I understand what you are trying to do, it isn't that difficult. What you need to do is read each line into a name and the remainder into marks. (input is read from stdin)
Then for each argument given on the command line, check if the first part matches the beginning of any grade in marks (the left size of the = sign). If it does, then save the grade (right side of the = sign) and set the found flag to 1.
After checking all marks against the first argument, if the found flag is 1, output the grade, otherwise output 0. Repeat for all command line arguments. (and then for all students in file) Let me know if you have questions:
#!/bin/bash
declare -i found=0 # initialize variables
declare -i grade=0
while read -r name marks; do # read each line into name & marks
printf "%s" "$name" # print student name
for i in "$#"; do # for each command line argument
found=0 # reset found (flag) 0
for j in $marks; do # for each set of marks check for match
[ $i = -${j%=*} ] && { found=1; grade=${j#*=}; } # if match save grade
done
[ $found -eq 1 ] && printf " %d" $grade || printf " 0" # print grade or 0
done
printf "\n" # print newline
done
exit 0
Output
$ bash marks_check.sh -ex1 -ex2 -ex3 < dat/marks.txt
Jack 5 3 0

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.

Resources