# Executing shell scripts with raycast (on macOS)

## Introduction

I want to feel more at home in the terminal. Recently, my work has been more backend-focused, meaning my head is always in the clouds! However, my fingers do not quite soar across the command line yet. Wanting to master this craft, I started learning shell scripting. I found it to be very rewarding, and I will share some of that in this blog.

I am by no means proficient, but I have already created some scripts that make my dev life way easier. Especially when combined with Raycast, executing scripts via keyboard shortcuts, some of my workflows became way less tedious.

The goals of this blog are:  
1. To show off some of the scripts that have been making my life easier.
2. To explain how to execute them conveniently with Raycast.

## Dictionary Script

I use this script to look up Japanese words, but jisho.org also translates English to Japanese, so it can be used both ways. 

```bash 
# store the clipboard content in a variable
selectedText=$(pbpaste)

# error if variable is empty
if [ -z "$selectedText" ]; then
  echo "No text on clipboard."
  osascript -e 'display notification "No text on clipboard" with title "Jisho Search Failed"'
  exit 1
fi

# encode the clipboard value for URL
encodedText=$(python3 -c "import urllib.parse; print(urllib.parse.quote(input()))" <<< "$selectedText")

# open jisho in standard browser, with search param 
open "https://jisho.org/search/${encodedText}"
```

I tried for a while to get a script working that copied selected text to the clipboard and immediately searched it, but that proved one step too many for my current abilities.

So, currently I use it like this:

1. Copy text with `⌘ + C`
2. Execute the script with `⌥ + D`

I'd love to have it work in one step, but these two keyboard shortcuts are quite close. For now, it's convenient enough.

Before we move on to the next script, let me show you how to set up the `⌥ + D` part in Raycast. 

## Script Commands in Raycast

I am going to assume you already use Raycast. If not, why not install it to try my script? It might just make your life much easier too. Alternatively, keep reading and at the end of the blog I will offer an alternative way to execute the script. 

We can make Raycast read scripts from a dedicated folder. That way adding more scripts after the first becomes really easy. Let's make a folder in home using `~/`. 

```bash 
# first make the folder
$ mkdir ~/scripts
# let's start editing our script
$ nano ~/scripts/jisho.sh
```
Once you are editing jisho.sh, copy and paste the script into it. As you can see below we need to add a little bit extra for Raycast. (to close nano: `^ + O` → `↵` → `^ + X`)

```bash 
#!/bin/bash

# Required parameters:
# @raycast.schemaVersion 1
# @raycast.title Jisho
# @raycast.mode Compact

# Optional parameters:
# @raycast.icon 🤖

# Documentation:
# @raycast.description Selected text to jisho search 
# @raycast.author Cas

selectedText=$(pbpaste)

if [ -z "$selectedText" ]; then
  echo "No text on clipboard."
  osascript -e 'display notification "No text on clipboard" with title "Jisho Search Failed"'
  exit 1
fi

encodedText=$(python3 -c "import urllib.parse; print(urllib.parse.quote(input()))" <<< "$selectedText")

open "https://jisho.org/search/${encodedText}"
```

Now we have to set up Raycast to read scripts from our folder. Open Raycast and search for `General` or `Raycast settings`, open that up and then → `Extensions` → `Script Commands`. 

![raycast.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588855/5d346a9e-3ed8-f4b2-3ce6-4eb5814ca504.png)

Click the Add Directories button and add your scripts directory. Finally, we want to open Raycast from scratch and start typing "Reload Script Directories", then run that command. 

Now we can return to the extensions tab, and add a hotkey for our script! If you try it out it should work! 

## Daily Report Script

After work, I post a daily report to Slack for coworkers. It's more or less always formatted the same way, containing:

1. Things I did today
2. Things I will do next

The content changes only a little bit day by day, so I always base it off the previous day's report, to speed things up. However, there are still a lot of steps involved.

1. Open the reports directory
2. Find yesterday's report in ~/reports/year/month/
3. Open the report
4. Copy its content
5. Create and name today's report
6. Open today's report and paste yesterday's into it

At the end of the day, sometimes we just want to go home. So, let's automate the entire thing and execute it on a shortcut. 

```bash 
$ nano ~/scripts/daily-report.sh
```

Copy and paste the following script (to close nano: `^ + O` → `↵` → `^ + X`)

```bash 
#!/bin/bash

# Required parameters:
# @raycast.schemaVersion 1
# @raycast.title Slack-report
# @raycast.mode Compact

# Optional parameters:
# @raycast.icon 🤖

# Documentation:
# @raycast.description Create a daily report from the contents of the previous report
# @raycast.author Cas

# set this to where you save your reports
base_dir=~/daily-slack-reports

# set this to your local timezone
year=$(TZ="Asia/Tokyo" date "+%Y")
month=$(TZ="Asia/Tokyo" date "+%m")
day=$(TZ="Asia/Tokyo" date "+%d")

echo $year   #2024
echo $month  #07
echo $day    #16

year_dir="$base_dir/$year" 
month_dir="$year_dir/$month"
mkdir -p "$month_dir" #creates /year/month only if it doesn't exist

today_file="$month_dir/$day.md"

# this gets all the markdown files in the month, removes the paths to them and selects the latest date
latest_file=$(ls -v $month_dir/*.md 2>/dev/null | grep -E '[0-9]+\.md$' | tail -n 1)

# if month is newly created, we get the reports content from the latest date of last month
# as is often the case, the edge cases are the hardest to make work...
if [ -z "$latest_file" ]; then
    prev_month=$((month - 1))
    month_year=$year
    if [ "$prev_month" -lt 1 ]; then
        prev_month=12
        month_year=$(($year - 1))
    fi
    previous_month_dir="$base_dir/$month_year/$prev_month"
    latest_file=$(ls -v $previous_month_dir/*.md 2>/dev/null | tail -n 1)
fi

# if a latest file is found, copy to today
if [ -n "$latest_file" ]; then
    cp "$latest_file" "$today_file"
    echo "Contents copied from $latest_file to $today_file"
fi

# open the directory (in your code editor), and today's file in it
code -n -r "$base_dir" 
code -r "$today_file"
```

The last part assumes that the code command is set up to use your editor. You might have to edit it. 

To set up for VS Code, open VS Code, hit `⇧ + ⌘ + P`, start typing `Shell Command: Install 'code' command in PATH` and run that. It should work now! 

## Can we not use Raycast please?

Fine..., although I really recommend giving Raycast a try, we can execute the script via an alias as well:

```bash 
$ echo 'alias daily-report="~/scripts/daily-report.sh"' >> ~/.zshrc
$ echo 'alias daily-report="~/scripts/daily-report.sh"' >> ~/.bashrc
$ source ~/.zshrc
$ source ~/.bashrc
```

now you can just:

```bash 
$ daily-report
```

## Wrap-up 

I just had a good idea. What if I created another script that posts the latest report to the correct slack channel via webhook? 

I guess coming up with ideas is half the work of productivity hacks like these. Maybe that is why I wrote this blog. I hope it will help you automate a workflow of your own. If it does, please share what you did in a comment!