Homepage    |    GitHub    |    API    |    FAQ

How to chain commands in Terminal App for joplin-instant-mail

I'd like to avoid calling ~/.joplin-bin/bin/joplin multiple times (once for each command).

Instead I'd like to make a single call to ~/.joplin-bin/bin/joplin and combine multiple commands into a single call, for example like this:
$ ~/.joplin-bin/bin/joplin "use Inbox ; mktodo 'foo1' ; mktodo 'foo2' ; 'sync'"

When I try as shown in the example I get this error message:

No such command: use Inbox ; mktodo 'foo1' ; mktodo 'foo2' ; 'sync'

Is there any trick to chain commands together for a single call to the terminal app?

I brought this up in the context of joplin-instant-mail and one idea was to go via API, but that has multiple drawbacks on the shared host where my email is processed and todos auto-added to joplin, see this thread on joplin-instant-mail , which is why I'm looking for an option to do this from the command line.

I don't think this is currently possible, but maybe @laurent can answer this. Chaining commands would certainly be a nice feature.

Indeed it's not possible but if someone wants to make a pull request for it, it shouldn't be too hard to add. In ClientClient/app/app.js, there's a part that parse and process the current command. So instead of processing one, you'd loop on the commands.

Thank you @tessus @laurent for shedding light on this!

Glad to know it wasn't just a syntax misunderstanding on my side!

I hardly know any js, so even though a solution inside would we preferable, does anyone have an idea for a workaround i could try myself from the shell?

Just wondering, because typing commands one after the other into the terminal app works, so getting them in via pipe, stdin or file or something separated by line breaks seems like it could work?

I'll have a look at it. I should have time this evening.

No that won't work, the app needs to start and stop between each command which I agree is not very efficient. The alternative would be to start the app as a server (with joplin server start) and use the API to achieve what you want to do. Although sync is currently not supported via the API.

Thank you @tessus and @laurent !!

I checked the code, but it looks like it's gonna be a bit more complicated. The current logic is as follows:

  • at one point the rest of argv is parsed
  • the first element in the array is the command
  • the remaining elements are arguments

I have to figure out a way to parse that correctly. If no double quotes are used a ; is a command separator for the shell. e.g. joplin use myfolder; ls would first switch to myfolder, and then list the local directory.

Quoting and parsing the separator will not be so easy, especially if the separator is not surrounded by a space on each side.

joplin "use \"folder with space\"; ls"

I have to think about this, because I'm no js expert and I don't know how command line arguments are parsed internally.

e.g. how do I create a folder named:

folder with a " in it

or

folder with ; in it

Hi @tessus

Thank you for having a look!

AFAIK a common way of dealing with quotes could be like this:

joplin 'use "folder with space" ; ls'

I.e. everything in single quotes will be a single argument and it is ok to use double quotes inside.

Either way Joplin is great, I use it every day. This is a possible improvement, but Joplin is great anyway :star_struck: Thank you everybody involved in creating and maintaining it :pray:

Doesn't joplin use popular command line parsing tools? For example, commandjs/yargjs, it is annoying to handle these manually. .

No, we don't. I think Laurent wrote the parsing himself, since only a few --options are available and none of them are officially supported.

The problem right now is that at that point the option parsing has already been done and only the rest of argv is parsed.

As I said, I'm not a JS expert, so I wouldn't know, if I could use the parsing tools you mentioned just for the rest of the argv or if I had to use it for everything.

Also, I'm not sure if those tools would help. In other languages they still require you to manually parse the rest of the argv and only take care of the options/arguments.
However, if we used something like -c "command" -c "2nd command" this might work though.

Yes, these tools should have considered the array parameters, there is really no need to reinvent the wheel

In terms of UI, I think we should make it explicit that it is a batch command, and then pass the commands as arguments. For example like this:

joplin batch 'mkbook one; mkbook two; sync'

Not sure about the ";" separator as that might be a reserved character in some shells. Maybe just a comma would work.

Then the trick is to check the first argument of the command and if it's "batch" to loop on the provided argument.

Right, that might work better than without the keyword. Still there are a few things to work out:

joplin batch 'mkbook "with space"; mkbook "with \' and ;"; sync'

Different shells behave slightly differently and I guess whatever shell somebody is using defines the syntax that that person needs to follow for passing arguments to joplin.

As soon as arguments arrive, inside joplin you can do anything you want I guess :sunglasses:

Some tools, e.g. sed, allow to define separators on the fly. Theoretically very nice, but many people don't get this and use / as separator for unix filenames and then escape every / with \ which gets very ahrd to read ...

Using a standard separator (semicolon) used by shells would have the advantage that many people would immediately understand I guess?

The quotes should take care of the semicolon in the middle if anyone really wants to use it in a folder name. Space, semicolon and several other characters loose their special meaning when used inside quotes, e.g. ...
$ echo '; echo "foo ; echo foo"'
produces
; echo "foo ; echo foo"

... so everything inside the single quotes is treated as a single argument (to echo in this example).

This exampe
joplin batch 'mkbook one; mkbook two; sync'
should work perfectly, let's try:
$ echo batch 'mkbook one; mkbook two; sync'
produces
batch mkbook one; mkbook two; sync

Without quotes it does not work:
$ echo batch mkbook one; mkbook two; sync
batch mkbook one
zsh: command not found: mkbook

... so it's the quotes that make all the difference :slight_smile: