I want to take integers as command line arguments into a shell script and doing basic string manipulations with these integers.
For example:
# POSIX Shell
user#host $ ./script 3 4 1 5
^
| * * *
| * * * *
| *
| * * * * *
------------
user#host $
And some pseudocode to demonstrate, how I would like to achieve this:
# Pseudocode
#!/bin/sh
for arg in $#; do
echo $((arg * '* '))
done
# Or something similar with a loop ;-)
If someone got an idea that could lead me into the right direction in a pure POSIX Shell like /bin/sh, that would be very nice!
There's a lot of ways to skin this cat. Here's one:
#!/bin/bash
array=( "$#" )
function repeat() { num="${2:-100}"; printf -- "$1%.0s" $(seq 1 $num); printf '\n'; }
arraylength=${#array[#]}
for (( i=0; i<${arraylength}; i++ ));
do
if [[ ${array[$i]} -ge $max ]]; then
max=${array[$i]}
fi
printf '%s' '| '
repeat '* ' ${array[$i]}
done
repeat '-' $(( $max+$max+3 ))
Or doing it in awk:
echo "3 4 1 5" | awk 'function a(b,c){r="";while(c-->0)r=r b;print r}{for(i=1;i<=NF;++i){printf "| ";a("* ",$i);if(m<$i)m=$i}}END{a("-",(m*2)+3)}'
I believe this is the crux of what you're looking for, and should be scrupulously POSIXly compliant, too:
#! /bin/sh
for n; do
printf '| ';
i=0;
while [ $i -lt $n ]; do
printf '* ';
i=$(( i + 1 ));
done
printf '\n';
done
$ cat script
#!/bin/sh
for arg in "$#"; do
yes | tr y '*' | head -n "$arg" | xargs
done
$ ./script 3 4 1 5
* * *
* * * *
*
* * * * *
Alternative:
$ cat script2
#!/bin/sh
for arg in "$#"; do
tr '\0' '*' < /dev/zero | head -c "$arg" | sed 's/./* /g'
echo
done
$ ./script2 3 4 1 5
* * *
* * * *
*
* * * * *
I am trying to create below pattern design in Shell script, i have written below codes for the same but i am not getting expected results. can someone help on this?
Desired output-->
*
**
***
****
*****
#! /bin/sh
for ((i=1; i<=5; i++))
do
for ((j=1; j<=5; j++))
do
if [ $j > 6-$i ]
then
echo -n "* "
elif [ $j == 6-$i ]
then
echo -n "* "
else
echo -n " "
fi
done
echo
done
Current output-->
*****
*****
*****
*****
*****
You will need to use another loop to print the pre-pending spaces.
for ((i=1;i<=5;i++)); # Loop to create 5 lines of text
do
for((k=1;k<=(5-i);k++)); # Loop to padd prepending spaces
do
printf "%s" " ";
done;
for ((j=1;j<=i;j++)); # Loop to create asterix
do
printf "%s" "*";
done;
printf "\n"; # Print the carriage return
done
to make the minimum number of changes to your code to get it working; you need to do number comparison, and number addition in order to get your check statements to work:
for ((i=1; i<=5; i++)) ; do
for ((j=1; j<=5; j++)) ; do
if [ $j -ge $((6-$i)) ] ; then
echo -n "* "
else
echo -n " "
fi
done
echo
done
new output:
*
* *
* * *
* * * *
* * * * *
It doesn't need so much arithmetic, you can use printf to add the padding:
n=5
line=''
char='*'
for ((i=1; i<=n; i++)); do
line+=$char
printf "%*s\n" $n "$line"
done
If you want the triangle leaning the other way:
printf "%-*s\n" $n "$line"
This question already has answers here:
Command not found error in Bash variable assignment
(5 answers)
Closed 3 years ago.
echo "Enter the current time: "
read h m s
s = `expr $n + 1`
if [ $n -eq 60 ]; then
s = 0
m = `expr $m + 1`
if [ $m -eq 60 ]; then
m = 0
h = `expr $h + 1`
if [ $h -eq 24 ]; then
h = 0
fi
fi
fi
echo "The time after one second is $h $m $n"
If you type s = 0 in a bash prompt, you will get bash: s: command not found.
The correct way to assign a variable in bash is s=0 without the spaces.
I'm getting an error saying
"line 6: [: : integer expression expected"
and I can't figure out what to do to fix it. I'm trying to write a script to print out 200 equations, the equations should be of form "i * j = k" where i is an integer between 1 and 10, j is between 1 and 20, k is the product of i and j.
#!/bin/bash
for i in {1..200..1}
do
if [ "$i" -gt 0 ] && [ "$i" -lt 11 ] && [ "$j" -gt 0 ] && [ "$j" -lt 21 ]
then
i = 1
j = 1
k = $(($i * $j))
echo $i * $j = $k
((i++))
((j++))
fi
done
In your script, both i and j get initialized to 1, which means your entire loop echoes 1 * 1 = 1, 200 times. Furthermore, j is not defined the first time your if statement tests $j, hence you got the error message "line 6: [: : integer expression expected".
One way of printing 200 equations, with combinations of i and j, where i is an int between 1 and 10, and j is between 1 and 20, is as follows:
#!/bin/bash
for (( i = 1; i <= 10; i++ )); do
for (( j = 1; j <= 20; j++ )); do
k=$(( i * j )) # Note no space before/after equal sign
echo "$i * $j = $k" # Note the quotation mark
done
done
Or you can do the same thing in a different format as follows:
#!/bin/bash
for i in {1..10}; do
for j in {1..20}; do
k=$(( i * j ))
echo "$i * $j = $k"
done
done
This way, both i and j get initialized before any statements are executed, and you can set the max and min restrictions on both within the loops.
How would I do something like:
ceiling(N/500)
N representing a number.
But in a linux Bash script
Why use external script languages? You get floor by default. To get ceil, do
$ divide=8; by=3; (( result=(divide+by-1)/by )); echo $result
3
$ divide=9; by=3; (( result=(divide+by-1)/by )); echo $result
3
$ divide=10; by=3; (( result=(divide+by-1)/by )); echo $result
4
$ divide=11; by=3; (( result=(divide+by-1)/by )); echo $result
4
$ divide=12; by=3; (( result=(divide+by-1)/by )); echo $result
4
$ divide=13; by=3; (( result=(divide+by-1)/by )); echo $result
5
....
To take negative numbers into account you can beef it up a bit. Probably cleaner ways out there but for starters
$ divide=-10; by=10; neg=; if [ $divide -lt 0 ]; then (( divide=-divide )); neg=1; fi; (( result=(divide+by-1)/by )); if [ $neg ]; then (( result=-result )); fi; echo $result
-1
$ divide=10; by=10; neg=; if [ $divide -lt 0 ]; then (( divide=-divide )); neg=1; fi; (( result=(divide+by-1)/by )); if [ $neg ]; then (( result=-result )); fi; echo $result
1
(Edited to switch let ... to (( ... )).)
Call out to a scripting language with a ceil function. Given $NUMBER:
python -c "from math import ceil; print ceil($NUMBER/500.0)"
or
perl -w -e "use POSIX; print ceil($NUMBER/500.0), qq{\n}"
Here's a solution using bc (which should be installed just about everywhere):
ceiling_divide() {
ceiling_result=`echo "($1 + $2 - 1)/$2" | bc`
}
Here's another purely in bash:
# Call it with two numbers.
# It has no error checking.
# It places the result in a global since return() will sometimes truncate at 255.
# Short form from comments (thanks: Jonathan Leffler)
ceiling_divide() {
ceiling_result=$((($1+$2-1)/$2))
}
# Long drawn out form.
ceiling_divide() {
# Normal integer divide.
ceiling_result=$(($1/$2))
# If there is any remainder...
if [ $(($1%$2)) -gt 0 ]; then
# rount up to the next integer
ceiling_result=$((ceiling_result + 1))
fi
# debugging
# echo $ceiling_result
}
You can use awk
#!/bin/bash
number="$1"
divisor="$2"
ceiling() {
awk -vnumber="$number" -vdiv="$divisor" '
function ceiling(x){return x%1 ? int(x)+1 : x}
BEGIN{ print ceiling(number/div) }'
}
ceiling
output
$ ./shell.sh 1.234 500
1
Or if there's a choice, you can use a better shell that
does floating point, eg Zsh
integer ceiling_result
ceiling_divide() {
ceiling_result=$(($1/$2))
echo $((ceiling_result+1))
}
ceiling_divide 1.234 500
Expanding a bit on Kalle's great answer, here's the algorithm nicely packed in a function:
ceildiv() {
local num=$1
local div=$2
echo $(( (num + div - 1) / div ))
}
or as a one-liner:
ceildiv(){ echo $((($1+$2-1)/$2)); }
If you want to get fancy, you could use a more robust version validates input to check if they're numerical, also handles negative numbers:
ceildiv() {
local num=${1:-0}
local div=${2:-1}
if ! ((div)); then
return 1
fi
if ((num >= 0)); then
echo $(( (num + div - 1) / div ))
else
echo $(( -(-num + div - 1) / div ))
fi
}
This uses a "fake" ceil for negative numbers, to the highest absolute integer, ie, -10 / 3 = -4 and not -3 as it should, as -3 > -4. If you want a "true" ceil, use $(( num / div )) instead after the else
And then use it like:
$ ceildiv 10 3
4
$ ceildiv 501 500
2
$ ceildiv 0 3
0
$ ceildiv -10 1
-10
$ ceildiv -10 3
-4
Mathematically, the function of ceiling can be define with floor, ceiling(x) = -floor(-x). And, floor is the default when converting a positive float to integer.
if [ $N -gt 0 ]; then expr 1 - $(expr $(expr 1 - $N) / 500); else expr $N / 500; fi
Ref. https://en.wikipedia.org/wiki/Floor_and_ceiling_functions
You can use jq if you have it installed. It's "sed for JSON", but I find it surprisingly handy for simple tasks like this too.
Examples:
$ echo 10.001 | jq '.|ceil'
11
$ jq -n '-10.001 | ceil'
-10
Floor () {
DIVIDEND=${1}
DIVISOR=${2}
RESULT=$(( ( ${DIVIDEND} - ( ${DIVIDEND} % ${DIVISOR}) )/${DIVISOR} ))
echo ${RESULT}
}
R=$( Floor 8 3 )
echo ${R}
Ceiling () {
DIVIDEND=${1}
DIVISOR=${2}
$(( ( ( ${DIVIDEND} - ( ${DIVIDEND} % ${DIVISOR}) )/${DIVISOR} ) + 1 ))
echo ${RESULT}
}
R=$( Ceiling 8 3 )
echo ${R}
If you have a string representation of a decimal number, bash does support ceiling using printf function like this:
$ printf %.4f 0.12345
0.1235
But if you need to do some math using decimals, you can use bc -l that by default scales to 20 decimals, then use the result with printf to round it.
printf %.3f $(echo '(5+50*3/20 + (19*2)/7 )' | bc -l)
17.929
This is a simple solution using Awk:
If you want the ceil of ($a/$b) use
echo "$a $b" | awk '{print int( ($1/$2) + 1 )}'
and the floor use
echo "$a $b" | awk '{print int($1/$2)}'
Note that I just echo the dividend '$a' as the first field of the line to awk and the divisor '$b' as the second.
Some more concise Awk logic
awk '
function ceil(ip) {
print ip%1 ? int(ip)+1 : ip
}
BEGIN {
ceil(1000/500)
ceil(1001/500)
}
'
Result
2
3
This function wont't add 1, if the division returns a non-floating number.
function ceiling {
DIVIDEND=${1}
DIVISOR=${2}
if [ $(( DIVIDEND % DIVISOR )) -gt 0 ]; then
RESULT=$(( ( ( $DIVIDEND - ( $DIVIDEND % $DIVISOR ) ) / $DIVISOR ) + 1 ))
else
RESULT=$(( $DIVIDEND / $DIVISOR ))
fi
echo $RESULT
}
Use it like this:
echo $( ceiling 100 33 )
> 4
Some more concise Awk logic
awk '
function ceil(ip) {
print ip%1 ? int(ip)+1 : ip
}
BEGIN {
ceil(1000/500)
ceil(1001/500)
}
'
Result
2
3
Using the gorgeous 'printf' 1 will round up to the next integer
printf %.0f $float
or
printf %.0f `your calculation formula`
or
printf %.0f $(your calculation formula)
ref: how to remove decimal from a variable?
Without specifying any function, we can use the following awk script:
echo x y | awk '{ r=$1 % $2; q=$1/y; if (r != 0) q=int(q+1); print q}'
Not sure this one get any logical error or not. Please correct.
If you are already familiar with the Python library, then rather than learn bc, you might want to define this bash function:
pc () { pyexpr="from math import *; print($#)"; python -c "$pyexpr"; }
Then:
pc "ceil(3/4)"
1
but also any valid python expression works:
pc pi / 4
0.7853981633974483
pc "'\n'.join(['Pythagoras said that %3.2f^2 + %3.2f^2 is always %3.2f'
% (sin(ai), cos(ai), sin(ai)**2 + cos(ai)**2)
for ai in [pi / 4 * k for k in range(8)]])"
Pythagoras said that 0.00^2 + 1.00^2 is always 1.00
Pythagoras said that 0.71^2 + 0.71^2 is always 1.00
Pythagoras said that 1.00^2 + 0.00^2 is always 1.00
Pythagoras said that 0.71^2 + -0.71^2 is always 1.00
Pythagoras said that 0.00^2 + -1.00^2 is always 1.00
Pythagoras said that -0.71^2 + -0.71^2 is always 1.00
Pythagoras said that -1.00^2 + -0.00^2 is always 1.00
Pythagoras said that -0.71^2 + 0.71^2 is always 1.00