Absolute value of a number - bash

I want to take the absolute of a number by the following code in bash:
#!/bin/bash
echo "Enter the first file name: "
read first
echo "Enter the second file name: "
read second
s1=$(stat --format=%s "$first")
s2=$(stat -c '%s' "$second")
res= expr $s2 - $s1
if [ "$res" -lt 0 ]
then
res=$res \* -1
fi
echo $res
Now the problem I am facing is in the if statement, no matter what I changes it always goes in the if, I tried to put [[ ]] around the statement but nothing.
Here is the error:
./p6.sh: line 13: [: : integer expression expected

You might just take ${var#-}.
${var#Pattern} Remove from $var the shortest part of $Pattern that matches the front end of $var. tdlp
Example:
s2=5; s1=4
s3=$((s1-s2))
echo $s3
-1
echo ${s3#-}
1

$ s2=5 s1=4
$ echo $s2 $s1
5 4
$ res= expr $s2 - $s1
1
$ echo $res
What's actually happening on the fourth line is that res is being set to nothing and exported for the expr command. Thus, when you run [ "$res" -lt 0 ] res is expanding to nothing and you see the error.
You could just use an arithmetic expression:
$ (( res=s2-s1 ))
$ echo $res
1
Arithmetic context guarantees the result will be an integer, so even if all your terms are undefined to begin with, you will get an integer result (namely zero).
$ (( res = whoknows - whocares )); echo $res
0
Alternatively, you can tell the shell that res is an integer by declaring it as such:
$ declare -i res
$ res=s2-s1
The interesting thing here is that the right hand side of an assignment is treated in arithmetic context, so you don't need the $ for the expansions.

I know this thread is WAY old at this point, but I wanted to share a function I wrote that could help with this:
abs() {
[[ $[ $# ] -lt 0 ]] && echo "$[ ($#) * -1 ]" || echo "$[ $# ]"
}
This will take any mathematical/numeric expression as an argument and return the absolute value. For instance: abs -4 => 4 or abs 5-8 => 3

A workaround: try to eliminate the minus sign.
with sed
x=-12
x=$( sed "s/-//" <<< $x )
echo $x
12
Checking the first character with parameter expansion
x=-12
[[ ${x:0:1} = '-' ]] && x=${x:1} || :
echo $x
12
This syntax is a ternary opeartor. The colon ':' is the do-nothing instruction.
or substitute the '-' sign with nothing (again parameter expansion)
x=-12
echo ${x/-/}
12
Personally, scripting bash appears easier to me when I think string-first.

I translated this solution to bash. I like it more than the accepted string manipulation method or other conditionals because it keeps the abs() process inside the mathematical section
abs_x=$(( x * ((x>0) - (x<0)) ))
x=-3
abs_x= -3 * (0-1) = 3
x=4
abs_x= 4 * (1-0) = 4

For the purist, assuming bash and a relatively recent one (I tested on 4.2 and 5.1):
abs() {
declare -i _value
_value=$1
(( _value < 0 )) && _value=$(( _value * -1 ))
printf "%d\n" $_value
}

If you don't care about the math and only the result matters, you may use
echo $res | awk -F- '{print $NF}'

The simplest solution:
res="${res/#-}"
Deletes only one / occurrence if - is at the first # character.

Related

How can I add x number of characters at the beginning of a string?

I'm trying to add x number of 0 at the beginning of a string, the number of characters missing is stored on $x, but how can I add them into the string?
echo "$exampleString" | sed -i 's/^/0/'
sed works just fine, but the number of 0 must be typed manually instead of getting from a calculated variable
You could use printf as such:
x=5
string="test"
printf '%0*d%s\n' "$x" 0 "$string"```
Here is a simple loop to calculate this.
Each step in the loop prepends a 0 to the result.
#!/bin/bash
text='example'
x=5
i=0
prefix='0'
until [ $i -eq $x ]; do
text="${prefix}${text}";
((i++))
done
echo "text is: $text"

How to test a dynamic substring variable in a conditional in shellscript?

This code check if the last 4 characters of the variable $1 correspond to the string in variable $2 anticipated by a dot.
if [ "${1: -4}" == ".$2" ]; then
return 0
else
return 1
fi
// true with $1 = example.doc and $2 = doc
// false with $1 = example.docx and $2 = doc
how can I replace the hardcoded 4 with the following variable $CHECKER_EXTENSION calculated like this?
LENGTH_EXTENSION=${#2}
CHECKER_EXTENSION=$(( LENGTH_EXTENSION + 1 ))
Thanks in advance
You don't need to strip the leading characters from $1, since bash's [[ ]] can do wildard-style pattern matching:
if [[ "$1" = *".$2" ]]; then
...
Note that you must use [[ ]], and not [ ], to get pattern-matching rather than simple string equality testing. Also, having the * unquoted but .$2 in quotes means the * will be treated as a wildcard, but $2 will be matched literally even if it contains wildcardish characters. If you want $2 to also be treated as a pattern (e.g. you could use [Jj][Pp][Gg] to match "jpg" and "JPG" and combinations), leave off the quotes:
if [[ "$1" = *.$2 ]]; then
Oh, and the quotes around $1 don't matter in this particular situation; but I tend to double-quote variables unless there's a specific reason not to.
The offset is interpreted as an arithmetic expression (the same syntax as inside $(( ... ))), so you can write:
if [ "${1: -CHECKER_EXTENSION}" == ".$2" ]; then
You can even eliminate the CHECKER_EXTENSION variable and write:
if [ "${1: -(${#2} + 1)}" == ".$2" ]; then
You may use it like this:
myfunc() {
local num_ext=$(( ${#2} + 1 ))
[[ "${1: -$num_ext}" = ".$2" ]]
}
If your execution environment meets the following criteria, you can choose a more concise method.
The requirement is to check if the extension is as expected
I'm using Bash
#!/usr/bin/env bash
if [[ ${1##*.} == "${2}" ]]; then
echo "same"
else
echo "not same"
fi
Execute test.
# test case 1
$ ./sample.sh test.txt txt
same
# test case 2
$ ./sample.sh test.exe txt
not same
# test case 3
$ ./sample.sh test.exe.txt txt
same
# test case 4
$ ./sample.sh test.txt.exe txt
not same
# test case 5
$ ./sample.sh test.txt.exe exe
same

I am getting expr syntax errors in bash shell for a simple program

#!/bin/bash
clear
echo "Enter a number"
read a
s = 0
while [ $a -gt 0 ]
do
r = ` expr $a % 10 `
s = ` expr $s + $r `
a = ` expr $a / 10 `
done
echo "sum of digits is = $s"
This is my code guys .
I am getting a bunch of expr syntax errors.
I am using the bash shell.
Thanks!
Your error is caused by the spaces surrounding the = in the assignments, the following replacements should work (I prefer $() to using backticks since they're much easier to nest):
s=0
r=$(expr $a % 10)
s=$(expr $s + $r)
a=$(expr $a / 10)
For example, s = 0 (with the spaces) does not set the variable s to zero, rather it tries to run the command s with the two arguments, = and 0.
However, it's not really necessary to call the external expr1 to do mathematical manipulation and capture the output to a variable. That's because bash itself can do this well enough without resorting to output capture (see ARITHMETIC EVALUATION in the bash man page):
#!/bin/bash
clear
read -p "Enter a number: " number
((sum = 0))
while [[ $number -gt 0 ]]; do
((sum += number % 10))
((number /= 10))
done
echo "Sum of digits is $sum"
You'll notice I've made some other minor changes which I believe enhances the readability, but you could revert back to the your original code if you wish and just use the ((expression)) method rather than expr.
1 If you don't mind calling external executables, there's no need for a loop in bash, you could instead use sneakier methods:
#!/bin/bash
clear
read -p "Enter a number: " number
echo "Sum of digits is $(grep -o . <<<$number | paste -sd+ | bc)"
But, to be brutally honest, I think I prefer the readable solution :-)

[0]-bash: [: 0*1%8: integer expression expected

Can someone point out what is wrong with the output.
for i in {0..127} ; do
echo -n [$i]
if [ $i*$j%8 -eq 0 ]; then
echo "\n"
fi
mytool -c "read 0x1540:0xa0:$i*$j"
done
I am trying to format the output into rows containing 8 items each.
I tried the suggestion below and modified my code to
for i in {0..8} ; for j in {0..16}; do
echo -n [$i*$j]
if [[ $i*$j%8 == 0 ]]; then
echo
fi
mytool -c "read 0x1540:0xa0:$i*$j"
done
Above with for i in {0..8} ; for j in {0..16}
I am expecting this to be a nested for loop.I am not very sure if this is how I do a nested loop in bash.
Still the output is not as I expect it.
My output looks like
[0]0x3
[1]0x4
[2]0x21
[3]0x1
[4]0x0
[5]0x0
[6]0x4
[7]0x41
[8]0x84
[9]0x80
[10]0x0
[11]0x0
[12]0x3
[13]0x0
[14]0x43
[15]0x49
[16]0x53
[17]0x43
[18]0x4f
[19]0x2d
[20]0x49
[21]0x4e
[22]0x43
[23]0x20
[24]0x0
[25]0x0
[26]0x9
[27]0x3a
[28]0x37
[29]0x34
[30]0x39
[31]0x34
I want [0] to [7] in ROW1
[8] to [15] in ROW2
and so on.
Use (( )) if you want to do math.
if ((i * j % 8 == 0)); then
Given your problem description I suggest a bit of a rewrite.
for i in {0..15}; do
for j in {0..7}; do
echo -n "[$((i * 8 + j))]"
mytool -c "read 0x1540:0xa0:$i*$j"
done
echo
done
The test command ([ is an alias for test, not syntax) requires the expression to be built up from multiple arguments. This means spaces are critical to separate operators and operands. Since each part is a separate argument, you also need to quote the * so that the shell does not expand it as a file glob prior to calling test/[.
if [ "$i" "*" "$j" % 8 -eq 0 ]; then
The test command expects 7 separate arguments here: $i, *, $j, %, -eq, and 0, which it then assembles into an expression to evaluate. It will not parse an arbitrary string into an expression.
As noted by John Kugelman, there are easier ways to accomplish such arithmetic in bash.

How to Get a Substring Using Positive and Negative Indexes in Bash

What I want is pretty simple. Given a string 'this is my test string,' I want to return the substring from the 2nd position to the 2nd to last position. Something like:
substring 'this is my test string' 1,-1. I know I can get stuff from the beginning of the string using cut, but I'm not sure how I can calculate easily from the end of the string. Help?
Turns out I can do this with awk pretty easily as follows:
echo 'this is my test string' | awk '{ print substr( $0, 2, length($0)-2 ) }'
Be cleaner in awk, python, perl, etc. but here's one way to do it:
#!/usr/bin/bash
msg="this is my test string"
start=2
len=$((${#msg} - ${start} - 2))
echo $len
echo ${msg:2:$len}
results in is is my test stri
You can do this with just pure bash
$ string="i.am.a.stupid.fool.are.you?"
$ echo ${string: 2:$((${#string}-4))}
am.a.stupid.fool.are.yo
Look ma, no global variables or forks (except for the obvious printf) and thoroughly tested:
substring()
{
# Extract substring with positive or negative indexes
# #param $1: String
# #param $2: Start (default start of string)
# #param $3: Length (default until end of string)
local -i strlen="${#1}"
local -i start="${2-0}"
local -i length="${3-${#1}}"
if [[ "$start" -lt 0 ]]
then
let start+=$strlen
fi
if [[ "$length" -lt 0 ]]
then
let length+=$strlen
let length-=$start
fi
if [[ "$length" -lt 0 ]]
then
return
fi
printf %s "${1:$start:$length}"
}

Resources