Under windows, when I need to perform a basic calculations, I use a built-in calculator. Now I would like to find out what is the common way if you only have a shell.
Thanks
From this web page (for csh and derivatives, since you asked):
% # x = (354 - 128 + 52 * 5 / 3)
% echo Result is $x
Result is 174
and
% set y = (354 - 128 + 52 / 3)
% echo Result is $y
Result is 354 - 128 + 52 / 3
notice the different results.
Personally, I stick to /bin/sh and call awk or something (for maximal portability), or others have exhibited the bash approach.
You can use dc. Or bc.
There are many good solutions given here, but the 'classic' way to do arithmetic in the shell is with expr:
$ expr 1 + 1
2
expr has a sensible return value, so that it succeeds when the expression evaluates to a non-zero value allowing code (in a Bourne shell) like:
$ op="1 + 1"
$ if expr $op > /dev/null; then echo "$op is not zero"; fi
1 + 1 is not zero
or (if using a shell that supports arrays):
$ op=(8 \* 3)
$ if expr "${op[#]}" > /dev/null; then echo "${op[#]} is not zero"; fi
8 * 3 is not zero
Note that the if syntax in Bourne shells is completely different than in the csh family, so this is slightly less useful and you need to check against the value of #?.
Bash supports basic (integer only) arithmetic inside $(( )):
$ echo $(( 100 / 3 ))
33
$ myvar="56"
$ echo $(( $myvar + 12 ))
68
$ echo $(( $myvar - $myvar ))
0
$ myvar=$(( $myvar + 1 ))
$ echo $myvar
57
(example copied straight from the IBM link)
More in-depth discussion of bash arithmetic
And you can always use the python interpreter, it's normally included in linux distros.
http://docs.python.org/tutorial/introduction.html#using-python-as-a-calculator
$ python
Python 2.6.2 (r262:71605, Apr 14 2009, 22:40:02) [MSC v.1500 32 bit (Intel)]
Type "help", "copyright", "credits" or "license" for more information.
>>> 2+2
4
>>> # This is a comment
... 2+2
4
>>> 2+2 # and a comment on the same line as code
4
>>> (50-5*6)/4
5
>>> # Integer division returns the floor:
... 7/3
2
>>> 7/-3
-3
>>> # use float to get floating point results.
>>> 7/3.0
2.3333333333333335
The equal sign ('=') is used to assign a value to a variable. Afterwards, no result is displayed before the next interactive prompt:
>>> width = 20
>>> height = 5*9
>>> width * height
900
And of course there's the math module which should solve most of your calculator needs.
>>> import math
>>> math.pi
3.1415926535897931
>>> math.e
2.7182818284590451
>>> math.cos() # cosine
>>> math.sqrt()
>>> math.log()
>>> math.log10()
You can also use Perl easily where bc or expr are not powerful enough:
$ perl5.8 -e '$a=1+2; print "$a\n"'
3
Alternative option is to use the built in BC command
Related
So I'm trying to create a random number limit the range from 1 to 10. There is a slightly different syntax between the two and I don't know if there any difference between those.
$(($RANDOM % 10 +1))
I tried this and it's working fine.
$(( ( RANDOM % 10 ) + 1 )). Including an extra () between RANDOM % 10 and +1 seems to work the same as the code above. But it has only one $ instead of 2.
Nothing. % has higher precedence (the same as *) than +, so the unparenthesized version is equivalent to the explicitly parenthesized one.
I originally missed that the second one also used RANDOM instead of $RANDOM. In an arithmetic context, a string is treated as an identifier, and is (recursively) expanded until you get an integer. If at any point the string is not a defined parameter, the value 0 is used instead. For example:
$ foo=bar
$ bar=6
$ echo $((bar))
6
$ echo $((foo))
6
In the last case, $foo expands to bar, which expands to 6.
IMO, it's better to use the explicit expansion. If the parameter isn't set due to a typo, you'll get an explicit error
$ foo=6
$ echo $((10 + $fo))
bash: 10 + : syntax error: operand expected (error token is "+ ")
rather than a silent "success" that might not be intended
$ echo $((10 + fo))
10 # ?? Why not 16?
I have string variables MIN and SEC (minute and seconds).
MIN = "1"
SEC = "34"
I want to do calculations on these.
TOTSEC = MIN*60 + SEC
I tried:
expr $SEC + $MIN * 60
Result:
expr: non-numeric argument
Let it be known I am running busybox on a custom microcomputer and so have no access to bash,bc, and that other solution provides.
In sh, by which I'll assume you mean a POSIX shell, your best option is to use Arithmetic Expansion:
$ MIN=1
$ SEC=34
$ TOTSEC=$(( MIN * 60 + SEC ))
$ printf '%d\n' "$TOTSEC"
94
In csh however, the built-in math works quite differently:
% set MIN = 1
% set SEC = 34
% # TOTSEC = ( $MIN * 60 + $SEC )
% printf '%d\n' "$TOTSEC"
94
According to the man page, the # command permits numeric calculations to be performed and the result assigned to a variable.
Note that the expr command is external to the shell, so it should be usable from either one.
In sh:
$ TOTSEC=$(expr "$MIN" \* 60 + "$SEC")
And in csh:
% set TOTSEC = `expr "$MIN" \* 60 + "$SEC"`
Note: your sh may not be POSIX compliant. Most likely, it's ash, which is the ancestor of dash and FreeBSD's /bin/sh. You'll need to test in your environment.
Case scenario:
$ cat Status.txt
1,connected
2,connected
3,connected
4,connected
5,connected
6,connected
7,disconnected
8,disconnected
9,disconnected
10,disconnected
11,disconnected
12,disconnected
13,disconnected
14,connected
15,connected
16,connected
17,disconnected
18,connected
19,connected
20,connected
21,disconnected
22,disconnected
23,disconnected
24,disconnected
25,disconnected
26,disconnected
27,disconnected
28,disconnected
29,disconnected
30,connected
As can be seen, there are "hollows", understanding them as lines with the "disconnected" value inside the sequence file.
I want, in fact, to detect these "holes", but it would be useful if I could set a minimum n of missing numbers in the sequence.
I.e: for ' n=5' a detectable hole would be the 7... 13 part, as there are at least 5 "disconnected" in a row on the sequence. However, the missing 17 should not be considered as detectable in this case. Again, at line 21 whe get a valid disconnection.
Something like:
$ detector Status.txt -n 5 --pattern connected
7
21
... that could be interpreted like:
- Missing more than 5 "connected" starting at 7.
- Missing more than 5 "connected" starting at 21.
I need to script this on Linux shell, so I was thinking about programing some loop, parsing strings and so on, but I feel like if this could be done by using linux shell tools and maybe some simpler programming. Is there a way?
Even when small programs like csvtool are a valid solution, some more common Linux commands (like grep, cut, awk, sed, wc... etc) could be worth for me when working with embedded devices.
#!/usr/bin/env bash
last_connected=0
min_hole_size=${1:-5} # default to 5, or take an argument from the command line
while IFS=, read -r num state; do
if [[ $state = connected ]]; then
if (( (num-last_connected) > (min_hole_size+1) )); then
echo "Found a hole running from $((last_connected + 1)) to $((num - 1))"
fi
last_connected=$num
fi
done
# Special case: Need to also handle a hole that's still open at EOF.
if [[ $state != connected ]] && (( num - last_connected > min_hole_size )); then
echo "Found a hole running from $((last_connected + 1)) to $num"
fi
...emits, given your file on stdin (./detect-holes <in.txt):
Found a hole running from 7 to 13
Found a hole running from 21 to 29
See:
BashFAQ #1 - How can I read a file (data stream, variable) line-by-line (and/or field-by-field)?
The conditional expression -- the [[ ]] syntax used to make it safe to do string comparisons without quoting expansions.
Arithmetic comparison syntax -- valid in $(( )) in all POSIX-compliant shells; also available without the expansion side effects as (( )) as a bash extension.
This is the perfect use case for awk, since the machinery of line reading, column splitting, and matching is all built in. The only tricky bit is getting the command line argument to your script, but it's not too bad:
#!/usr/bin/env bash
awk -v window="$1" -F, '
BEGIN { if (window=="") {window = 1} }
$2=="disconnected"{if (consecutive==0){start=NR}; consecutive++}
$2!="disconnected"{if (consecutive>window){print start}; consecutive=0}
END {if (consecutive>window){print start}}'
The window value is supplied as the first command line argument; left out, it defaults to 1, which means "display the start of gaps with at least two consecutive disconnections". Probably could have a better name. You can give it 0 to include single disconnections. Sample output below. (Note that I added series of 2 disconnections at the end to test the failure that Charles metions).
njv#organon:~/tmp$ ./tst.sh 0 < status.txt # any number of disconnections
7
17
21
31
njv#organon:~/tmp$ ./tst.sh < status.txt # at least 2 disconnections
7
21
31
njv#organon:~/tmp$ ./tst.sh 8 < status.txt # at least 9 disconnections
21
Awk solution:
detector.awk script:
#!/bin/awk -f
BEGIN { FS="," }
$2 == "disconnected"{
if (f && NR-c==nr) c++;
else { f=1; c++; nr=NR }
}
$2 == "connected"{
if (f) {
if (c > n) {
printf "- Missing more than 5 \042connected\042 starting at %d.\n", nr
}
f=c=0
}
}
Usage:
awk -f detector.awk -v n=5 status.txt
The output:
- Missing more than 5 "connected" starting at 7.
- Missing more than 5 "connected" starting at 21.
I need to do some integer math in csh (and no, other shells are not an option, nor is bc, nor is perl, nor is python, period).
In bash my task would look like
seq 1 1 10 > m.txt #supplied from elsewhere
a=2 #supplied from elsewhere
b=3 #supplied from elsewhere
head -n $[$a*$b] m.txt # the line in question
then the question is Is there an expression in csh that computes $[$a*$b] inline?
I know that I can do # c = $a * $b in csh, but that's not inline. I did a little bit of googling and searching SO, but no success so far, so any help is greatly appreciated!
Are your use of square-brackets meant to indicate an array notation or matrix math? csh has no such built-in features.
ELSE, if you mean like bash $(($a * $b)), you can use csh cmd-substitution with backquotes to give you
head -n `expr $a \* $b` m.txt
Note that if your goal was to avoid spawning extra processes, this does not meet your goal, but it is "in-line"
Edit I see I mistyped as $( $a * $b ), see inline correction above.
IHTH.
Without using something outside of the shell, no.
The usual culprit for math from old school shell scripts is expr:
head -n `expr $a \* $b` m.txt
but if that's just as verboten as bc et al, then you're out of luck. Period.
Yes, but it's not pretty:
% seq 1 1 10 > m.txt
% set a = 2
% set b = 3
% head -n `# tmp = $a * $b ; echo $tmp ; unset tmp` m.txt
1
2
3
4
5
6
Note that this will clobber $tmp if you happen to have a variable of that name, so choose a unique name.
(Though I wonder why bc, perl, and python are not an option.)
Is there a way to find the size(memory used) of shell variable from command line, without using C ?
This tells you how many characters are in the value of a scalar variable named "var":
echo ${#var}
This tells you the number of elements in an array named "array":
echo ${#array[#]}
This tells you the number of characters in an element of an array:
echo ${#array[3]}
If you try to get the size of an array and you leave out the [#] index, you get the length of element 0:
$ array=(1 22 333 4444)
$ echo ${#array}
1
$ echo ${#array[#]}
4
$ echo ${#array[2]}
3
If you want the total length of all elements of an array, you could iterate over the array and add them up, you could use IFS and some steps similar to those below, or you could:
$ tmp="${array[*]}"
$ echo $(( ${#tmp} - ${#array[#]} + 1 ))
10
Beware of using the number of elements in an array as the index of the last element since Bash supports sparse arrays:
$ array=(1 22 333 4444 55555)
$ echo ${#array[#]}
5
$ array[9]=999999999
$ echo ${#array[#]}
6
$ echo ${array[${#array[#]} - 1]} # same as echo ${array[6 - 1]}
$ # only a newline is echoed since element 5 is empty (only if "nounset" option* is not set (default in most cases))
$ # when "nounset" option is set (possibly using command "set -u") then bash will print such error:
$ # bash: array[${#array[#]} - 1]: unbound variable
$ unset "array[1]" # always quote array elements when you unset them
$ echo ${#array[#]}
5
$ echo ${array[${#array[#]} - 1]} # same as echo ${array[5 - 1]}
55555
That was obviously not the last element. To get the last element:
$ echo ${array[#]: -1} # note the space before the minus sign
999999999
Note that in the upcoming Bash 4.2, you can do echo ${array[-1]} to get the last element. In versions prior to 4.2, you get a bad subscript error for negative subscripts.
To get the index of the last element:
$ idx=(${!array[#]})
$ echo ${idx[#]: -1}
9
Then you can do:
$ last=${idx[#]: -1}
$ echo ${array[last]}
999999999
To iterate over a sparse array:
for idx in ${!array[#]}
do
something_with ${array[idx]}
done
* I recommend avoiding nounset
wc can tell you how many characters and bytes are in a variable, and bash itself can tell you how many elements are in an array. If what you're looking for is how large bash's internal structures are for holding a specific variable then I don't believe that's available anywhere.
$ foo=42
$ bar=(1 2 3 4)
$ echo -n "$foo" | wc -c -m
2 2
$ echo "${#bar[#]}"
4
For a scalar variable, ${#VAR} gives you the length in characters. In a unibyte locale, this is the length in bytes. The size in bytes is the length of the name in bytes, plus the length of the value in bytes, plus a constant overhead.
LC_ALL=C
name=VAR
size=$(($#name + $#VAR)) # plus a small overhead
If the variable is exported, the size is roughly double.
LC_ALL=C
name=VAR
size=$((($#name + $#VAR) * 2)) # plus a small overhead
For an array variable, you need to sum up the lengths (again, in bytes) of the elements, and add a constant overhead per element plus a constant overhead for the array.
LC_ALL=C
name=VAR
size=$(($#name)) # plus a small overhead
for key in "${!VAR[#]}"; do
size=$((size + ${#key} + ${#VAR[$key]})) # plus a small overhead
done
Here's a minimally tested function that computes the approximate size occupied by a variable. Arrays and exports are taken into account, but not special read-only variables such as $RANDOM. The sizes have been observed on bash 4.2, different versions may have different overheads. You may need to adjust the constants depending on your system types and malloc implementation.
_sizeof_pointer=4
_sizeof_int=4
_malloc_granularity=16
_malloc_overhead=16
## Usage: compute_size VAR
## Print the amount of memory (in bytes) used by VAR.
## The extra bytes used by the memory allocator are not taken into account.
add_size () {
local IFS="+" this extra
set $(($1 + _malloc_overhead))
_size=$((_size + $1))
set $(($1 % _malloc_granularity))
[[ $1 -eq 0 ]] || _size=$((_size + _malloc_granularity - $1))
}
compute_size () {
local LC_ALL=C _size=0 _key
if eval "[ -z \${$1+1} ]"; then echo 0; return; fi
add_size $((_sizeof_pointer*5 + _sizeof_int*2)) # constant overhead
add_size ${#1} # the name
case $(declare -p $1) in
declare\ -x*)
eval "add_size \${#$1}" # the value
eval "add_size \$((\${#1} + \${#$1} + 2))" # the export string
;;
declare\ -a*)
eval 'for _key in "${!'$1'[#]}"; do
add_size $_key
add_size ${#'$1'[$_key]}
add_size $((_sizeof_pointer*4))
done'
add_size $((_sizeof_pointer*2 + _sizeof_int*2))
add_size $((_sizeof_pointer*4))
;;
*)
eval "add_size \${#$1}" # the value
;;
esac
echo $_size
}
${#VAR}
tells you the length of the string VAR