Creating a pseudo-array in dash, (POSIX).

Arrays in dash, (POSIX).

Hi gurus...

I am thinking of trying AudioScope.sh in pure POSIX so...
I need an array in dash, I know it is not possible but pseudo-arrays are.

I have two versions that work, the second is an idea from the WWW.
The first is what I would like to use.
There are problems in both...

The first has two problems:
1) It needs "eval" to create a number of variables "VAR0, VAR1, VAR2 ... VARn".
AND
2) I have no idea if there is a limit to how many variables POSIX allows.
The minimum I would need is 48000 and the maximum 65536.

The second is a modified version from the WWW and has a major problem as well as 'eval' and the 'maximum limit' too.
But the main one is I have no idea how to make it mutable.

So the questions are:

Is there a better method than either of mine that creates a pseudo-array that is fully mutable?
Is there a limit to the number of variables to create a pseudo-array in POSIX?
Is it possible to eliminate 'eval' to create a pseudo-array, (I suspect the answer is no)...

TIA...
This is mutable...

#!/usr/local/bin/dash
# MUTABLE! ;o)
# 'dash' Version 0.5.9

/bin/echo 'ab
b c
c$d
de
e \f\n
f/g
g~ h
h*i
i#j
j%k
kl
lm' > /tmp/array.txt

# *******************************************
# The main array generator,,,
INDEX=0
while IFS=$'\n' read -r ARRAY
do
	eval "MY_ARRAY${INDEX}='${ARRAY}'"
	INDEX=$(( INDEX+1 ))
done < /tmp/array.txt
# *******************************************

LENGTH=${INDEX}
/bin/echo "Array_length = ${LENGTH}"
# Basic test.
/bin/echo "Original array index 4 = ${MY_ARRAY4}"
MY_ARRAY4='1 2 3'
/bin/echo "Modified array index 4 = ${MY_ARRAY4}"
/bin/echo ""

INDEX=0
while [ ${INDEX} -le 12 ]
do
	eval /bin/echo "\${MY_ARRAY${INDEX}}"
	INDEX=$(( INDEX+1 ))
done

This is immutable.

#!/usr/local/bin/dash
# IMMUTABLE! ;o(
# 'dash' Version 0.5.9

/bin/echo 'a b,bc,cd \f\n,de,ef,f g,gh,h\i,ij,jk,kl,lm' > /tmp/array.txt

ARRAY=$( cat /tmp/array.txt )

orig_ifs="$IFS"
IFS=','

set -- $ARRAY

INDEX=1
while [ ${INDEX} -le 12 ]
do
	eval VAL=$( /bin/echo "\${${INDEX}}" )
	printf "%u = %s\n" "${INDEX}" "${VAL}"
	INDEX=$(( INDEX+1 ))
done
LENGTH=$(( INDEX-1 ))

#*Just basic tests.
/bin/echo ""
/bin/echo "Array_length = ${LENGTH}"
INDEX=10
eval /bin/echo "\${${INDEX}}"
INDEX=3
eval /bin/echo "\${${INDEX}}"
/bin/echo ""

IFS="$orig_ifs"
1 Like
#!/bin/dash
var="one|test|program"
oldIFS=$IFS
IFS="|"
set -- $var
echo "$1"
echo "$2"
echo "$3"      # more $9 == curly braces e.g. "${10}"
IFS=$oldIFS

I need it to be fully mutable.
So how can I change '$1' and put it back into var?

Execute the above, then call part of your scheme to assign each of the elements to your array. Personally, I would avoid dash if possible if you need features like typedef , declare , arrays.... AFAIK a Linux system with /bin/sh == dash also will have /bin/bash available, too. ---Not applicable to ARM linux and other minimized CE versions of Linux. A shebang with #!/bin/bash seems preferable. Unless of course this is all just meant for fun. You do realize that extensive workarounds for production systems are generally bad idea.

Also consider that other folks have encountered POSIX and have come up with code like:
Rich�s sh (POSIX shell) tricks

My comments above were focused primarily on Linux-like platforms.

2 Likes

How about a concept like this, mutable without eval .. :

#!/usr/local/bin/dash
set -- a b c
array() {
  _pos=$1
  _newval=$2
  _ARRAY= _i=-1 _val=
  shift 2
  for _val in "$@"
  do
    if [ $((_i+=1)) = $_pos ]; then
      _ARRAY="${_ARRAY}${_newval}
"
    else
      _ARRAY="${_ARRAY}${_val}
"
    fi
  done 
}

array 1 foo "$@"
oldIFS=$IFS
IFS="
"
set -- ${_ARRAY}

echo "Space as output field separator:
$@
"
echo "IFS as output field separator:
$*"

IFS=$oldIFS

Output:

Space as output field separator:
a foo c

IFS as output field separator:
a
foo
c
2 Likes

If your array elements do not contain <newline> characters and do not contain elements that are longer than 2047 bytes, you could always use a plain text file as your array with each line in the file being an array element. You can then use shell for and/or while loops to process array elements in sequence.

If you need random access to array elements, you could use ed to access or replace individual elements in your array directly without affecting other elements (lines) in your array (file).

Without knowing more about the data you want to put into your array and what you want to do with your array after you create it, there are lots and lots of possibilities (many of which might be completely impossible for what you might actually be trying to do).

@ Don...

Each element in the pseudo-array will be a ASCII number from 0 to 255 decimal the value of an 8 bit byte, (so therefore 1 to 3 characters long); a simple audio sample.
(However it could be a 2 byte word from 0 to 65535 decimal, depends how I feel; a more accurate sample if I decide.)
I used newlines as an element separator in one of my examples but any IFS value would probably be OK by me...

You mention ed and I have never, ever used it and I like the idea you put forwards so man page and www it is to see the possibiities.
Is there a limit to POSIX's number of variables, or is that limited to available real memory and element sizes?

@ Jim M...

I code for pure fun and I really love trying the _impossible_ and making languages do stuff they were not designed to do.

@ Scrutinzer...

Not in my local environment at the moment but will try your method out.

If you're used to using vi you've been using ed for as long as you've been using vi ; you just didn't know it. And, by the way, everything I say about ed in the rest of this note also applies to the ex utility. I usually use ed instead of ex because ed is usually smaller than ex , and, therefore, loads faster. There are a few small differences between ed and ex , but none of them apply to the examples used in the following discussion.

In vi when you hit escape (to exit text append mode) and issue a command that starts with a <colon>, everything you enter after the colon is an ed command.

For example the vi the :w filename command writes the current contents of the editing buffer to the file named filename. In ed the command w filename writes the current contents of the editing buffer to the file named filename.

In vi the command :100s/.*/123/ will change the contents of line 100 in the editing buffer to the string "123". In ed the command 100s/.*/123/ will change the contents of line 100 in the editing buffer to the string "123".

In vi the command :100,200g/^.$/s/^/0/ followed by the command :100,200g/^..$/s/^/0/ will add leading zero characters on any of lines 100 through 200 of your editing buffer that had only one or two characters to have three characters on those lines with leading zero fill. In ed the command 100,200g/^.$/s/^/0/ followed by the command 100,200g/^..$/s/^/0/ will add leading zero characters on any of lines 100 through 200 of your editing buffer that had only one or two characters to have three characters on those lines with leading zero fill.

According to POSIX, the number of variables you can have in the shell and the length of values assigned to those variables is limited by the amount of physical memory available to the shell and the address space available to the shell in its addressable data space. The length of lines in ed is further restricted because the behavior of ed is only defined if the file you are editing is a text file and the standard says that a file is not a text file if it contains a line containing more than LINE_MAX bytes (including the terminating <newline> character) and implementations are required to set the implementation's LINE_MAX configuration limit to a value no smaller than 2048.

Cheers,
Don

1 Like