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 Fish Shell for string processing.

Bash is great, but I really think bash is so legacy. And I wonder why we can not obtain better shell approach. This riddle has been a long time in mhy mind, but I haven’t got time to examine fish shell in real life.

So here I am. Porting my bash script into fish script. I found that, the fish script is tidier, than the bash counterpart.

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

Just like bash or any other shell, fish does not support multidimensional array. And it also doesn’t support array in array.

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 fish

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

FISH Solution

The Answer

Instead of IFS, we are going to use string join command, to extract variable in fish.

This approach saved a few lines. Ans easier to read the script.

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

# Extract flatten output to array
set tags (flatten $songs | sort -u)
string join ':' $tags

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

Environment

No need any special setup. Just run and voila..!


1: Array in FISH

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

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

Simple Array

Consider begin with simple array.

#!/usr/bin/env fish

set tags "rock" "jazz" "rock" "pop" "pop"
echo $tags

With output result as below:

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

FISH: Simple Array

We won’t discuss about associative array. Since I decide not to use data structure approach.

Using String Join Command

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

#!/usr/bin/env fish

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

string join \n\b $songs

With the result, similar as below record:

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

FISH: 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 fish

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

FISH: 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 fish
source ./MySongs.sh

string join \n\b $songs

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

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

for song in $songs
  echo $song | read -d ';' title tags
  echo "$title is $tags"
end

With the result as below lines of string.

FISH: Using Songs Module

❯ ./04-module-b.sh
Cantaloupe Island is 60s,jazz
Let It Be is 60s,rock
Knockin' on Heaven's Door is 70s,rock
Emotion is 70s,pop
The River is 

Notice how the read command has delimiter option.

echo $song | read -d ';' title tags

This read command, split 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 fish
source ./MySongs.sh

set -l tagss

for song in $songs
  echo $song | read -d ';' title tags
  set -a tagss $tags
end

string join ':' $tagss

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

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

FISH: Extracting Comma Separated String

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

  set -a tagss $tags

Flatten

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

Using two loops. One after another.

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

set -l tagss
set -l tags

for song_item in $songs
  echo $song_item | read -d ';' title tags
  set -a tagss $tags
end

for tags_item in $tagss
  echo $tags_item | read -d ',' -a tag_temp
  set -a tags $tag_temp
end

string join ':' $tagss
string join ':' $tags

With the result of a flattened array shown below.

❯ ./06-flatten-a.sh
60s,jazz:60s,rock:70s,rock:70s,pop
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 fish
source ./MySongs.sh

set -l tags
for song_item in $songs
  echo $song_item | read -d ';' title tags_temp

  for tags_item in $tags_temp
    echo $tags_item | read -d ',' -a tag_temp
    set -a tags $tag_temp
  end
end

string join ':' $tags

With the result exactly the same as previous flattened array.

FISH: 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 fish

function flatten
  set -l l_songs $argv
  set -l tags

  for song_item in $l_songs
    echo $song_item | read -d ';' title tags_temp

    for tags_item in $tags_temp
      echo $tags_item | read -d ',' -a tag_temp
      set -a tags $tag_temp
    end
  end

  string join \n $tags
end

Now, pay attention to the return values. Returning array in fish is, simply with string join command.

FISH: The Flatten Function

Now we can use code above in other script.

Unique

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

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

# Extract flatten output to array
set tags (flatten $songs | sort -u)
string join ':' $tags

With the result similar as below array:

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

FISH: Solving Unique Song

I don’t want to be perfect, or fish purist. I just want to know a little about fish.


What is Next 🤔?

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