Article Series

This article series discuss more than 30 different programming languages. Please read overview before you read any of the details.

Playing with Records Related Articles.

Where to Discuss?

Local Group

Preface

Goal: Exploring Ion Shell for string processing.

The ion shell from redox-os, has been in my radar since years ago. But this is the first time I experience this modern shell. The syntax is concise, powerfull.

I can wait patiently for redox-os to be happened. And for you all developer who work silently in the background. Keep the good job!

Reference Reading

Source Examples

You can obtain source examples here:


Common Use Case

Task: Get the unique tag string

Please read overview for more detail.

Data Structure Support

Unlike like bash, fish or any other shell, ion support array in array. But it is not easy to access. Slo decide to use plain string instead.

For learning purpose, we can explore fish capability, with CSV like data.

Prepopulated Data

Songs and Poetry

The data is simply an array containing multilines. Which each lines separated by semicolon, then separated by comma.

#!/usr/bin/env ion

let songs:[str] = [\
  "Cantaloupe Island;60s,jazz" \
  "Let It Be;60s,rock" \
  "Knockin' on Heaven's Door;70s,rock" \
  "Emotion;70s,pop" \
  "The River"
]

Ion Solution

The Answer

ion shell differ scalar variable and array variable. This makes the shell easier to return array variable.

#!/usr/bin/env ion
source ./MySongs.sh
source ./MyHelperFlatten.sh

# Extract flatten output to array
let tags_fl = $(flatten [@songs])
let tags_un = $(echo $tags_fl | sort -u)
echo $join(@split($tags_un \n) :)

Enough with introduction, at this point we should go straight to coding.

Environment

Normally, ion shell is not available in repository. In my artix linux, I can get ion-git shell from aur, where in other distribution, I have to compile the ion shell manually.

Ion Shell: Installing


1: Array in Ion shell

I decide to use plain data, instead of associative array.

Although plain data only consist of lines of string, we still have to examine a bit about array.

Simple Array

Consider begin with simple array.

#!/usr/bin/env ion
let tags:[str] = ["rock" "jazz" "rock" "pop" "pop"]

echo @tags
echo @tags[1]

With output result as below:

❯ ./01-tags.sh
rock jazz rock pop pop
jazz

Ion shell: Simple Array

Associative Array

Although we won’t use associative array. We can examine a bit. Now consider this form.

#!/usr/bin/env ion

let song:hmap[str] = [ \
  title='Cantaloupe Island' \
  tags=["60s" "jazz"] \
]

echo @song
echo @song[title]
echo @song[tags]

let tags = @song[tags]
let tags = [ @split($tags) ]
echo @tags[1]

With output result as below:

❯ ./02-map.sh
tags 60s jazz title Cantaloupe Island
Cantaloupe Island
60s jazz
jazz

Ion Shell: Output associative array

We still have no problem with code above. But unfortunately we cannot go further than this.

Using String Join Command

consider go straight with plain data. We can test join command. See the command in action. And examine how the command works.

#!/usr/bin/env ion

let songs = [\
  "Cantaloupe Island;60s,jazz" \
  "Let It Be;60s,rock" \
  "Knockin' on Heaven's Door;70s,rock" \
  "Emotion;70s,pop" \
  "The River"
]

echo $join(@songs \n)

With the result, similar as below record:

❯ ./03-songs.sh
Cantaloupe Island;60s,jazz
Let It Be;60s,rock
Knockin on Heavens Door;70s,rock
Emotion;70s,pop
The River

Ion Shell: Using String Join Command

No need complex structure.


2: Separating Module

Since we need to reuse the songs record multiple times, it is a good idea to separate the record structure from logic.

Songs Module

The code is pretty similar with above code, as shown as below:

#!/usr/bin/env ion

let songs:[str] = [\
  "Cantaloupe Island;60s,jazz" \
  "Let It Be;60s,rock" \
  "Knockin' on Heaven's Door;70s,rock" \
  "Emotion;70s,pop" \
  "The River"
]

Ion shell: The Songs Module Containing List of Record

Using Songs Module

Now we can have a very short code. With the result exactly the same as above code:

#!/usr/bin/env ion
source ./MySongs.sh

echo $join(@songs \n)

Or alternatively, we can alter the output, using read and loop.

#!/usr/bin/env ion
source ./MySongs.sh

for song in @songs
  let pair = [@split($song ;)]
  if test $len(@pair) -gt 1
    echo "@pair[0] is [@pair[1]]"
  end
end

With the result as below lines of string.

Ion Shell: Using Songs Module

❯ ./04-module-b.sh
Cantaloupe Island is [60s,jazz]
Let It Be is [60s,rock]
Knockin on Heavens Door is [70s,rock]
Emotion is [70s,pop]

Notice how the split command has delimiter ; option.

let pair = [@split($song ;)]

This split command, read variables based on, the this delimiter value.


3: Finishing The Task

Extract, Flatten, Unique

Extracting Data Structure

Based on the result above, we can go further, extracting the tags data.

#!/usr/bin/env ion
source ./MySongs.sh

let tagss = []

for song in @songs
  let pair = [@split($song ;)]
  if test $len(@pair) -gt 1
    let tagss ++= @pair[1]
  end
end

echo $join(@tagss ":")

With the result of array of array, as shown below.

❯ ./05-extract.sh
60s,jazz:60s,rock:70s,rock:70s,pop

Ion Shell: Extracting Comma Separated String

Notice how we populate the tagss array variable, by append value for each loop.

  let tagss ++= @pair[1]

Flatten

Now we can normalize, the separated values. Flatten all values into just single array.

Using two loops. One after another.

#!/usr/bin/env ion
source ./MySongs.sh

let tagss = []
let tags  = []

for song_item in @songs
  let pair = [@split($song_item ;)]
  if test $len(@pair) -gt 1
    let tagss ++= @pair[1]
  end
end

for tags_item in @tagss
  let tag_temp = [@split($tags_item ,)]
  let tags ++= [@tag_temp]
end

echo $join(@tags ":")

With the result of a flattened array shown below.

❯ ./06-flatten-a.sh
60s:jazz:60s:rock:70s:rock:70s:pop

I know it is a little bit long. This long code looks inefficient. We are going to make the code shorter.

Flatten Rewrite

We can rewrite above statement, using loop in a loop, to make the code shorter.

#!/usr/bin/env ion
source ./MySongs.sh
let tags  = []

for song_item in @songs
  let pair = [@split($song_item ;)]
  if test $len(@pair) -gt 1
    let tags ++= [@split(@pair[1] ,)]
  end
end

echo $join(@tags ":")

With the result exactly the same as previous flattened array.

Ion Shell: Flattening Array

Flatten Function

Since we are going to reuse the flatten approach above in other code. It is better to bundle the script in its own module.

#!/usr/bin/env ion

fn flatten songs:[str]
  let tags  = []

  for song_item in @songs
    let pair = [@split($song_item ;)]
    if test $len(@pair) -gt 1
      let tags ++= [@split(@pair[1] ,)]
    end
  end

  echo $join(@tags \n)
end

Again, pay attention to the return values. Returning array in ion shell is, simply with echo $join() command.

Ion Shell: The Flatten Function

Now we can use code above in other script.

Unique

Instead of solving the approach using pure ion, I prefer to use sort -u instead.

#!/usr/bin/env ion
source ./MySongs.sh
source ./MyHelperFlatten.sh

# Extract flatten output to array
flatten [@songs] | sort -u

With the result similar as below array:

❯ ./07-unique-a.sh
60s
70s
jazz
pop
rock

We still need to join this array. Now have a look at this code below:

I have to put the pipe in two lines to make it works.

#!/usr/bin/env ion
source ./MySongs.sh
source ./MyHelperFlatten.sh

# Extract flatten output to array
let tags_fl = $(flatten [@songs])
let tags_un = $(echo $tags_fl | sort -u)
echo $join(@split($tags_un \n) :)

With the result similar as below array:

❯ ./07-unique-b.sh
60s:70s:jazz:pop:rock

Ion Shell: Solving Unique Song

It is nice to know something rare such as ion shell.


What is Next 🤔?

Consider continue reading [ Ion Shell - Playing with Records - Part Two ].