Bash Number Limit

How to handle integers in bash with values larger than 2^63

I'd recommend using bc with its arbitrary precision.

Bash overflows at 263:

$ A=$(( 2**63 - 1 ))
$ echo $A
9223372036854775807
$ echo $(( A+1 ))
-9223372036854775808

bc can handle this:

$ bc <<< "$A+1"
9223372036854775808

These numbers have to be handled with bc for everything from now on, though. Using [[ ]], they don't seem to overflow, but comparison doesn't work properly:

$ B=$(bc <<< "$A+1")
$ echo $B
9223372036854775808
$ set -vx
$ [[ $B -gt -$A ]] && echo true
[[ $B -gt -$A ]] && echo true
+ [[ 9223372036854775808 -gt -9223372036854775807 ]]

And in arithmetic context (( )), they overflow:

$ echo $(( B ))
-9223372036854775808

so the comparison doesn't work either:

$ (( B > A )) && echo true || echo false
false

Handling them with bc:

$ bc <<< "$B > $A"
1

and since within (( )) non-zero results evaluate to true and zero to false, we can use

$ (( $(bc <<< "$B > $A") )) && echo true
true

Bash script size limitation?

Yes, this is a limitation with bash.

It's not a script size limit; rather it's a limit to the depth of the parser stack, which has the effect of restricting the complexity of certain constructs. In particular, it will restrict the number of elif clauses in an if statement to about 2500.

There is a longer analysis of this problem with respect to a different syntactic construct (iterated pipes) in my answer to a question on the Unix & Linux stackexchange site.

case statements don't have this limitation, and the sample you provide certainly looks like a good match for a case statement.

(The difference with case statements is that the grammar for if conditional statements, like that of pipe constructs, is right recursive, while the grammar for case statements is left recursive. The reason the limitation on if statements is different from the limitation on pipes is that the grammatical construct for an elif clause has one more symbol, so each repetition uses four stack slots rather than three.)

If the case statement doesn't work for you -- or even if it does -- you could try building a precompiled binary search tree of if statements:

if (( task_number < 8 )); then
if (( task_number < 4 )); then
if (( task_number < 2 )); then
if (( task_number < 1)); then
# do task 0
else
# do task 1
fi;
elif (( task_number < 3 )); then
# do task 2
else
# do task 3
fi
elif (( task_number < 6 )); then
if (( task_number < 5 )); then
# do task 4
else
# do task 5
fi
elif (( task_number < 7 )); then
# do task 6
else
# do task 7
fi
elif (( task_number < 12 )); then
if (( task_number < 10 )); then
if (( task_number < 9 )); then
# do task 8
else
# do task 9
fi
elif (( task_number < 11 )); then
# do task 10
else
# do task 11
fi
elif (( task_number < 14 )); then
if (( task_number < 13 )); then
# do task 12
else
# do task 13
fi
elif (( task_number < 15 )); then
# do task 14
else
# do task 15
fi

Because each complete if statement only occupies a single stack node after it is recognized, the complexity limitation will be on the nesting depth of the if statements rather than the number of clauses. As an additional bonus, it will execute a lot fewer comparisons in the average case.

If you have no alternative other than a sequential list of conditions, you can use separate if statements:

while :; do
if condition1; then
# do something
break; fi; if condition2; then
# do something
break; fi; if condition3; then
# do something
break; fi; if condition4; then
# do something
break; fi
# No alternative succeeded
break
done

The unconventional indent is intended to illustrate the simple program transformation: simply replace every elif with break;fi;if and surround the whole thing with a while (to provide the target for the breaks.)

Bash command line and input limit

The limit for the length of a command line is not imposed by the shell, but by the operating system. This limit is usually in the range of hundred kilobytes. POSIX denotes this limit ARG_MAX and on POSIX conformant systems you can query it with

$ getconf ARG_MAX    # Get argument limit in bytes

E.g. on Cygwin this is 32000, and on the different BSDs and Linux systems I use it is anywhere from 131072 to 2621440.

If you need to process a list of files exceeding this limit, you might want to look at the xargs utility, which calls a program repeatedly with a subset of arguments not exceeding ARG_MAX.

To answer your specific question, yes, it is possible to attempt to run a command with too long an argument list. The shell will error with a message along "argument list too long".

Note that the input to a program (as read on stdin or any other file descriptor) is not limited (only by available program resources). So if your shell script reads a string into a variable, you are not restricted by ARG_MAX. The restriction also does not apply to shell-builtins.

bash variable for upper limit of for loop number syntax

Don't use {...}; use a C-style for loop.

for ((i=1; i <= $n; i++)); do

Brace expansion occurs before parameter expansion, so you can't generate a variable-length sequence without using eval. This is also more efficient, as you don't need to generate a possibly large list of indices before the loop begins.

Limit the Number of Characters with Regex

First, your users are going to hate you if "incorrect format" is all the info you give them. Second, you're asking a lot of one regex. Yes, you might be able to do it with a regex, but have some pity of the guy maintaining it behind you. :)

I'll assume you are simply disallowing all non-alphas. You sure you don't want to automatically downcase it too? And as an aside, this will disallow extended characters, and firstname.lastname is a very euro-centric mindset...

declare -l username # forces values to lowercase
msg="Please use only letters with a single dot separator, and limit the total to 16 characters."
while read -p "Enter a Username (firstname.lastname): " username
do case "$username" in # check for
# too long | invalid chars | multidots
?????????????????*|*[^a-z.]*|*.*.*) echo "$msg" ;; # add A-Z if not downcasing
*.*) break;; # this is the format we want
*) echo "$msg" ;; # a catch-all in case we missed anything else
esac
done

Regexes are more powerful than globbing, but this is more readable to me. YMMV.

While Read Line - Limit Number of Lines

There are some ways to meet your requirements:

Method 1

Use head to display first few lines of a file.

head -n 2 order.csv | while read line;
do
order=$(echo $line | cut -d "," -f 1)
status=$(echo $line | cut -d "," -f 3)
echo "$order:$status"
done

Method 2

Use a for loop.

for i in {1..2}
do
read line
order=$(echo $line | cut -d "," -f 1)
status=$(echo $line | cut -d "," -f 3)
echo "$order:$status"
done < order.csv

Method 3

Use awk.

awk -F, 'NR <= 2 { print $1":"$3 }' order.csv

Bash - getting MAX value from a list of integers

Your loops are a bit wonky (technical term) and you are resetting maxLength to zero on every iteration of your loop. You want something a bit more like:

#!/bin/bash

fn="${1:-/dev/stdin}" ## read from file given as 1st argument (default stdin)

test -r "$fn" || { ## validate file is readable
printf "error: file not readable '%s'.\n" "$fn"
exit 1
}

declare -i maxlength=0 ## set maxlength before loop
maxname=

while IFS=, read -r area name host
do
test -n "$name" || continue ## if name not set get next line
len=${#name}
if [ "$len" -gt "$maxlength" ]; then ## test length against max
maxlength=$len ## update max if greater
maxname="$name" ## save name in maxname
fi
done <"$fn" ## feed loop by redirecting file

printf "maxname: %s (len: %d)\n" "$maxname" "$maxlength"

Example Use/Output

$ bash maxnm.sh <dat/maxnm.txt
maxname: script_name_12345678999999 (len: 26)

Look things over and let me know if you have further questions.



Related Topics



Leave a reply



Submit