arithmetic operation return negative value - sh

I have an arithemic operation between two big value as following :
#!/bin/sh
a=$((0x1000000*0x0001d2))
echo $a
# result: -771751936
# wrong may the root cause is the size of variable
Have any solution to fix that?
NOTE: I'm using a very limited shell (sh).

Related

/bin/sh Adding an element to the arguments array from within the script

Ultimate Goal
Essentially I have a while loop going through each command-line argument passed to the script, but want to set default behaviour if no arguments are passed.
Current solution idea
My plan for this is to test if there are any arguments prior to this while loop and if there aren't any, simply add the default arguments (flags in this case) to the argument array and let the script execute from there as normal.
Problem I'm having
Where I'm tripping up a little here is figuring out the syntax to add these default arguments to the arguments array itself (I can get values from it no problem).
This is what I have so far:
if test $# -eq 0
then
# ADD --default TO ARGUMENTS ARRAY HERE
fi
while test $# -gt 0
do
case "$1" in
--opt1) DO STUFF
;;
--opt2) DO MORE / OTHER STUFF
;;
--default) DO DEFAULT STUFF
;;
esac
shift
done
Following and altering examples using user-defined arrays I have tried:
if test $# -eq 0
then
$+=('--default')
fi
But just get the syntax error
syntax error near unexpected token `'--default''
sh does not have arrays other than the built-in command-line argument list, and thus no += operator for arrays.
You also seem to be missing a character; what you wrote evaluates to assigning (default) to the value of the variable $+ but this variable is not valid (though because there are no arrays, the parentheses cause an error already).
The shell allows you to use set to manipulate the built-in argument list.
if test $# -eq 0
then
set -- --default
fi
The condition requires the array to be empty, so we simply populate it with the single element you provided; in the general case, you could add stuff to the end with
set -- "$#" --default
or correspondingly to the beginning by putting something before "$#" (which of course contains the previous value of the argument list).

Variable not being recognized after "read"

-- Edit : Resolved. See answer.
Background:
I'm writing a shell that will perform some extra actions required on our system when someone resizes a database.
The shell is written in ksh (requirement), the OS is Solaris 5.10 .
The problem is with one of the checks, which verifies there's enough free space on the underlying OS.
Problem:
The check reads the df -k line for root, which is what I check in this step, and prints it to a file. I then "read" the contents into variables which I use in calculations.
Unfortunately, when I try to run an arithmetic operation on one of the variables, I get an error indicating it is null. And a debug output line I've placed after that line verifies that it is null... It lost it's value...
I've tried every method of doing this I could find online, they work when I run it manually, but not inside the shell file.
(* The file does have #!/usr/bin/ksh)
Code:
df -k | grep "rpool/ROOT" > dftest.out
RPOOL_NAME=""; declare -i TOTAL_SIZE=0; USED_SPACE=0; AVAILABLE_SPACE=0; AVAILABLE_PERCENT=0; RSIGN=""
read RPOOL_NAME TOTAL_SIZE USED_SPACE AVAILABLE_SPACE AVAILABLE_PERCENT RSIGN < dftest.out
\rm dftest.out
echo $RPOOL_NAME $TOTAL_SIZE $USED_SPACE $AVAILABLE_SPACE $AVAILABLE_PERCENT $RSIGN
((TOTAL_SIZE=$TOTAL_SIZE/1024))
This is the result:
DBResize.sh[11]: TOTAL_SIZE=/1024: syntax error
I'm pulling hairs at this point, any help would be appreciated.
The code you posted cannot produce the output you posted. Most obviously, the error is signalled at line 11 but you posted fewer than 11 lines of code. The previous lines may matter. Always post complete code when you ask for help.
More concretely, the declare command doesn't exist in ksh, it's a bash thing. You can achieve the same result with typeset (declare is a bash equivalent to typeset, but not all options are the same). Either you're executing this script with bash, or there's another error message about declare, or you've defined some additional commands including declare which may change the behavior of this code.
None of this should have an impact on the particular problem that you're posting about, however. The variables created by read remain assigned until the end of the subshell, i.e. until the code hits a ), the end of a pipe (left-hand side of the pipe only in ksh), etc.
About the use of declare or typeset, note that you're only declaring TOTAL_SIZE as an integer. For the other variables, you're just assigning a value which happens to consist exclusively of digits. It doesn't matter for the code you posted, but it's probably not what you meant.
One thing that may be happening is that grep matches nothing, and therefore read reads an empty line. You should check for errors. Use set -e in scripts to exit at the first error. (There are cases where set -e doesn't catch errors, but it's a good start.)
Another thing that may be happening is that df is splitting its output onto multiple lines because the first column containing the filesystem name is too large. To prevent this splitting, pass the option -P.
Using a temporary file is fragile: the code may be executed in a read-only directory, another process may want to access the same file at the same time... Here a temporary file is useless. Just pipe directly into read. In ksh (unlike most other sh variants including bash), the right-hand side of a pipe runs in the main shell, so assignments to variables in the right-hand side of a pipe remain available in the following commands.
It doesn't matter in this particular script, but you can use a variable without $ in an arithmetic expression. Using $ substitutes a string which can have confusing results, e.g. a='1+2'; $((a*3)) expands to 7. Not using $ uses the numerical value (in ksh, a='1+2'; $((a*3)) expands to 9; in some sh implementations you get an error because a's value is not numeric).
#!/usr/bin/ksh
set -e
typeset -i TOTAL_SIZE=0 USED_SPACE=0 AVAILABLE_SPACE=0 AVAILABLE_PERCENT=0
df -Pk | grep "rpool/ROOT" | read RPOOL_NAME TOTAL_SIZE USED_SPACE AVAILABLE_SPACE AVAILABLE_PERCENT RSIGN
echo $RPOOL_NAME $TOTAL_SIZE $USED_SPACE $AVAILABLE_SPACE $AVAILABLE_PERCENT $RSIGN
((TOTAL_SIZE=TOTAL_SIZE/1024))
Strange...when I get rid of your "declare" line, your original code seems to work perfectly well (at least with ksh on Linux)
The code :
#!/bin/ksh
df -k | grep "/home" > dftest.out
read RPOOL_NAME TOTAL_SIZE USED_SPACE AVAILABLE_SPACE AVAILABLE_PERCENT RSIGN < dftest.out
\rm dftest.out
echo $RPOOL_NAME $TOTAL_SIZE $USED_SPACE $AVAILABLE_SPACE $AVAILABLE_PERCENT $RSIGN
((TOTAL_SIZE=$TOTAL_SIZE/1024))
print $TOTAL_SIZE
The result :
32962416 5732492 25552588 19% /home
5598
Which are the value a simple df -k is returning. The variables seem to last.
For those interested, I have figured out that it is not possible to use "read" the way I was using it.
The variable values assigned by "read" simply "do not last".
To remedy this, I have applied the less than ideal solution of using the standard "while read" format, and inside the loop, echo selected variables into a variable file.
Once said file was created, I just "loaded" it.
(pseudo code:)
LOOP START
echo "VAR_A="$VAR_A"; VAR_B="$VAR_B";" > somefile.out
LOOP END
. somefile.out

Modification of non-creatable array value attempted, subscript -4

I am newbie to perl. My script was running for months together and now it is causing a problem and it wont send an email. The script actually sends 2 different graphs (line and bar) of the total number cases per week and its average.
This is the line that is throwing error.
$graph->set_legend(#week_start_dates[-4..-1]);
Error message-Modification of non-creatable array value attempted, subscript -4
Is it something to do with perl where you cannot increase the index on left.(-4,-5) etc.
Any thoughts much appreciated.Thanks
Yes, because subroutine parameters are aliases to the actual value, the value has to exist, and while it will create positive indexes if they don't exist, it doesn't lengthen the array to create negative indexes, because that would change the meaning of other indexes that do exist.
You can see this simply with:
perl -wle'#x = 1..3; sub{}->(#x[-4..-1])'
Modification of non-creatable array value attempted, subscript -4 at -e line 1.
So you should figure out why there are less than four week_start_dates.
Or if having less than four week_start_dates is acceptable, you could use the following:
#week_start_dates > 4 ? #week_start_dates[-4..-1] : #week_start_dates

Passing a variable to a command in a script

I've been searching all over the place and since I'm taking my first steps in PERL this might be one of he dumbest questions but here it goes.
So I'm creating a script to manage my windows and later bind it to keyboard shortcuts, so I I'm trying to run a command and passing some variables:
my $command = `wmctrl -r :ACTIVE: -e 0,0,0,$monitors->{1}->{'width'}/2,$monitors->{1}->{'height'}`;
But I get an error saying I'm not passing the right parameters to the command, but if I do this, everything works great:
my $test = $monitors->{1}->{'width'}/2;
my $command = `wmctrl -r :ACTIVE: -e 0,0,0,$test,$monitors->{1}->{'height'}`;
So do I really have to do this? assign it first to a variable and then pass it, or there's a more elegant way of doing it?
The backticks operator (or the qx{}) accepts A string which is (possibly) interpolated. So accepts string and not expression like $var/2.
Thats mean than the $variables ($var->{1}->{some} too) are expanded but not the arithmetic expressions.
Therefore your 2 step variant works, but not the first.
If you want evaluate an expression inside the string you can use the next:
my $ans=42;
print "The #{[ $ans/2 ]} is only the half of answer\n";
prints
The 21 is only the half of answer
but it is not very readable, so better and elegant is what you're already doing - calculate the command argument in andvace, and to the qx{} or backticks only pass the calculated $variables.

To fix a bug in Less' command by noting no. of parameters

This question is based on this thread.
The code
function man()
{
man "$1" > /tmp/manual; less /tmp/manual
}
Problem: if I use even one option, the command does not know where is the wanted-manual
For instance,
man -k find
gives me an error, since the reference is wrong. The command reads -k as the manual.
My attempt to solve the problem in pseudo-code
if no parameters
run the initial code
if one parameter
run: man "$2"
...
In other words, we need to add an option-check to the beginning such that
Pseudo-code
man $optional-option(s) "$n" > /tmp/manual; less /tmp/manual
where $n
n=1 if zero options
n=2 if 1 option
n=3 if 2 options
....
How can you make such an "option-check" that you can alter the value of $n?
Developed Problem: to make two if loops for the situations from n=1 to n=2
How about passing all the arguments
function man()
{
man $# > /tmp/manual; less /tmp/manual
}
What is the bug in less which you mention in the title?
First, you can pass all of your function's arguments to man by using $* or $#. You can read man sh for the precise details on the difference between the two; short story is to almost always use "$#" with double quotes.
Second, the temporary file is unnecessary. You could make this a little cleaner by piping the output of man directly to less:
function man() {
man "$#" | less
}
By the way, if you're just trying to use a different pager (man uses more and you want the fancier less) there's a commonly recognized PAGER environment variable that you can set to override the default pager. You could add this to your ~/.bashrc for instance to tell all programs to use less when displaying multiple screens of output:
export PAGER=less
To answer your precise question, you can check the number of arguments with $#:
if [ $# -eq 0 ]; then
: # No arguments
elif [ $# -eq 1 ]; then
: # One argument
# etc.
You might also find the shift command helpful. It renames $2 to $1, $3 to $2, and so on. It is often used in a loop to process command-line arguments one by one:
while [ $# -gt 1 ]; do
echo "Next argument is: $1"
shift
done
echo "Last argument is: $1"