TIL: VS Code tasks and bash scripts

I use Hugo for some static pages on the website. I wanted to convert some common actions that take a couple of steps into a single command. The one I tacked now is to create a new ‘Book’ page template in Hugo and open it in the editor. It’s a simple action, but took me a bit to figure out the basic stuff.

1. Create a VSCode task to execute the commands without opening terminal.

If the workspace doesn’t have a ‘tasks.json‘ already, create one
Press 'F1' - my shortcut key for "Show all commands" Select "Run task", and follow the prompts to create a new task. Select "Other" when selecting task type. VSCode will create a 'tasks.json'  file with a single task that prints 'Hello' on the command line.
If there’s already a ‘tasks.json‘ in the workspace folder, add a new task

Simplest way is to just copy a previous task and change details – label, type, command, args, problemMatcher, etc

2. Configure task and inputs

Label is just the name for the task.

"label": "New Book"

Type is the command type. In my case, this was just ‘shell

"type": "shell"

For command, we’ll just point to the shell script. I use VSC’s in-built variable, ‘${workspaceFolder}‘ to mark the path to the script.

"command": "${workspaceFolder}/pages/makebooks.sh"

I provided two arguments to the shell script—the workspace folder path and the name of the book to create.

"args": [
    "${workspaceFolder}",
    "${input:bookName}"
]

Finally, since I was using a user input ‘input:bookName‘, I had to create an inputs section too.

"inputs": [{
    "id": "bookName",
    "type": "promptString",
    "description": "Name of the book, please:",
    "default": "A book"
}]

Here, id is the variable name of the input which we can use to reference it in the task section. type is the type of input. It can be a promptString asking user to type a string, a pickString providing user options to choose from, or a command which runs a command and returns the output as input. description is the prompt shown to the user when input is asked, and default is the default input.

That’s the task creation done. I still have to make the actual shell script to execute, but this bit is done.

3. Create a shell script to execute when the task is run

Create a new file with the name and path chosen in ‘command‘ section above. Also make the shell script executable.

Now open the shell script in VSC, and make it do the work.

First, just switch to the subfolder where hugo related content is kept.

cd "$1"/pages

$1 is the first argument to the script, which in our case is the first item in the ‘args‘ provided in the task description—the home folder of the VSC workspace.

TIL: Escape inputs with "" to send them to other commands.
It took me a bit of googling to figure out that the easiest way to take user inputs and pass them, escaped, to other commands is to just enclose them in "". Specially, like in my case when there are spaces in book names or paths.
Another alternative that didn’t work for me was using printf: printf -v bookName "%q" $2. This will escape the $2 arg and save it in variable bookName.

Next, create the new books page:

hugo new books/"$2".md

Now, check if the command succeeded (it fails if a book with that name already exists).

if [ $? -eq 0 ]; then

If it succeeded, open the book file for editing:

code content/books/"$2".md

TIL

  • Using $? to get the exit code of the last command. 0 means successfully completed, anything higher means failure.
  • How to use the shell if-then-else
  • Using test with the [...] syntax

That’s it. We’re done. Now when I need to create a new book page, all I do is press F1, select Run task, type in the book name, and it opens a new page with the template for the book.


Here’s how the two files looked at the end:

tasks.json:

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "New Book",
            "type": "shell",
            "command": "${workspaceFolder}/pages/makebooks.sh",
            "args": [
                "${workspaceFolder}",
                "${input:bookName}"
            ],
            "problemMatcher": []
        }
    ],
    "inputs": [{
        "id": "bookName",
        "type": "promptString",
        "description": "Name of the book, please:",
        "default": "A book"
    }]
}

makebooks.sh:

# Go to pages folder where we can execute Hugo commands
cd "$1/pages"

# Create new book page
hugo new books/"$2".md

# If page created successfully, open it for editing
if [ $? -eq 0 ]; then
    code content/books/"$2".md
fi