Utility scripts for the CLI?
I would like some utility shell scripts (preferably in bash or zsh) for using the 1Password CLI to perform the following functions:
1. Create a new secure note.
2. Delete a secure note (completely delete it, don't leave it in the Trash).
3. Update the contents of a secure note with new data.
4. Rename a secure note (the note under the original name should be deleted).
5. Copy a secure note to another secure note with a different name (the note under the original name should remain).
I'm willing for this to be done with documents instead of a secure notes, if that turns out to be easier.
I believe that all of these can be accomplished via a combination of multiple CLI calls, "jq" calls, and scripting logic. I'm willing to write these, but before I start to "reinvent the wheel", I'm wondering if anyone has already created scripts that perform any of these functions.
Thank you in advance.
1Password Version: Not Provided
Extension Version: Not Provided
OS Version: Not Provided
Sync Type: Not Provided
Comments
-
OOPS! I forgot that there's no way to empty the Trash folder using the CLI, so some of these are currently impossible. I think #1 and #5 can be performed, however ... and maybe #4.
0 -
So ... please, please, please (!!!) could you folks implement a way to empty the Trash via the CLI?
If that is done, then I think I could write these 5 scripts, and I'll post them here. This will give CLI users a number of added capabilities, without the 1Password programmers having to do any work to develop anything else (besides the CLI Trash-emptying capability).
0 -
Hi @HippoMan,
Unfortunately only 1 seems to be possible. 2, 4, 5 would require empty trash. 3 would require the ability to update an existing item which doesn't yet exist in the tool.
I'm very much looking forward to us spending the time to do item management properly.
Rick
0 -
If we could empty the Trash via the CLI, then #3 would also be possible, as follows ...
Step 1: Query the uuid of the original item.
Step 2: Create a new item with the same name as the original item, containing the updated data.
Step 3: If step 2 succeeds, move the original item to the Trash using its uuid.
Step 4: Empty the Trash.So when the 1Password staff works on enhancing the item-management capabilities, I request that a CLI empty-the-Trash function be the first CLI enhancement that you implement. That way, I could write all 5 scripts and give people all those capabilities while the rest of the CLI enhancement work progresses over time.
Alternatively, I'm willing to sign a non-disclosure agreement and write the CLI empty-the-Trash function myself, and give it to you folks.
0 -
That makes sense. I do think it's a little sad to do trash/delete/create-new as a poor man's edit, but you're right that it'd work.
Have you considered throwing your Hippo-Hat into ring here? https://discussions.agilebits.com/discussion/88757/help-shape-the-future-of-the-1password-command-line-tool :)
Rick
0 -
Yes, I read that, and I already sent an email about this to ROT13(wbof@ntvyrovgf.pbz) a little more than a week ago.
0 -
Actually, I just now think I figured out how to write all these scripts using the currently-implemented CLI ...
After an item is created via
op create document xxx --vault=yyy
, it shows up underop list items
. Then, afterop delete item UUID
is invoked using that item's uuid, the item no longer shows up underop list items
, even though it still is returned viaop get item UUID
andop get item NAME
.So, I can write an
item_exists
function which simply does anop list items
and looks to see if there is an item with the given uuid among the returned results. All the scripts would use this function to determine whether or not there already is an existing item, and this means that after doing anop delete item ...
, that item will appear to be non-existent to the scripts.This way, the scripts would work with the same view as the vault view of the 1Password web page. This
item_exists
methodology is hacky and inefficient, but I think it will work.Does anyone see any problems with this approach? If not, I'm going to start on this script work in my spare time.
Excelsior! (Latin expression for "Onward and Upward!")
0 -
Here's my initial, quick-and-dirty attempt for the
item_exists
function. It's a shell script that is meant to be called within other scripts to let them know whether a given item exists outside of the Trash. It either returns 0 or something non-zero, depending upon whether or not the specified item exists. Comments? ...#!/bin/zsh -f prog=${0##*/} op=/usr/local/bin/op jq=/usr/bin/jq tmp0=/tmp/.${prog}.0.$$ tmp1=/tmp/.${prog}.1.$$ quit() { /bin/rm -f ${tmp0} ${tmp1} exit ${1} } trap 'quit 1' 1 2 3 15 [[ $# -lt 1 ]] && { print - " usage: ${prog} item-uuid [ optional 'op list items' arguments ] returns 0 if the item exists, otherwise non-zero optional arguments could contain '--vault=XXX', for example " exit 1 } uuid="${1}" shift filter=".[] | select(.uuid == \"${uuid}\") | .uuid" # Use temp files so that we can catch the `op` return code # before passing any results to `jq`, and likewise for # catching the `jq` return code before checking its results. ${op} list items "${@}" >${tmp0} || quit 1 ${jq} -M "${filter}" <${tmp0} >${tmp1} || quit 1 if [[ -s "${tmp1}" ]] then quit 0 else quit 1 fi
0 -
... and assuming the above script is named
op-uuid-exists
, the following script accepts either a uuid or an item name on the command line, and it determines whether there is one and only one item with that name or uuid existing outside of the Trash. Its return code is 0 if there is indeed only one single non-Trash match. If there are no non-Trash matches, its return code is -1, and if there are multiple non-Trash matches, the return code is the number of matches (2 or greater). Comments? ...#!/bin/zsh -f prog=${0##*/} op=/usr/local/bin/op jq=/usr/bin/jq tmp0=/tmp/.${prog}.0.$$ tmp1=/tmp/.${prog}.1.$$ quit() { /bin/rm -f ${tmp0} ${tmp1} exit ${1} } trap 'quit 1' 1 2 3 15 [[ $# -lt 1 ]] && { print - " usage: ${prog} item [ optional 'op get item' arguments ] returns 0 if the item exists, otherwise non-zero optional arguments could contain '--vault=XXX', for example " quit -2 } item="${1}" shift # Use temp files so that we can catch the `op` return code # before passing any results to `jq`, and likewise for # catching the `jq` return code before checking its results. ${op} get item "${item}" "${@}" >${tmp0} || quit 1 [[ -s "${tmp0}" ]] || quit 1 filter='.uuid' ${jq} -M --raw-output "${filter}" <${tmp0} >${tmp1} || quit 1 found=0 while read uuid do /usr/local/bin/op-uuid-exists "${uuid}" || continue (( found = found + 1 )) done <${tmp1} case "${found}" in (1) quit 0 ;; (0) quit -1 ;; (*) print - "${found} matches for ${item} ${*}" quit ${found} ;; esac
0 -
PS: in the first script, the
exit 1
after the usage message should be changed toquit -2
. It's too late for me to edit the comment containing the first script.0 -
Thank you. Yes, I'll clean up the scripts later and take your suggestion. These are just proofs of concept, at the moment. Once I get these going, I'll probably put them up on github.
0 -
... and here's script #1 from my original list of proposed scripts:
op-create-document
. I'm doing all the scripts for documents first. Once they're all working, I'll then write the corresponding scripts for secure notes (or maybe enhance these scripts to handle both documents and secure notes), because secure notes are a bit more complicated. This script won't create a document if one with the same name already exists outside of the Trash ...#!/bin/zsh -f prog=${0##*/} op=/usr/local/bin/op jq=/usr/bin/jq tmp=/tmp/.${prog}.0.$$ quit() { /bin/rm -rf ${tmp} exit ${1} } trap 'quit 1' 1 2 3 15 stdin= while getopts :iI o do case "${o}" in ([iI]) stdin=t ;; (*) print -r -- "${0##*/}: invalid option: -${o}" exit 1 ;; esac done shift $(( ${OPTIND} - 1 )) [[ $# -lt 1 ]] && { print - " usage: ${prog} [ -iI ] item-name [ optional 'op create document' arguments ] returns 0 if the item exists, otherwise non-zero option -i or -I means take item contents from stdin optional arguments could contain '--vault=XXX' or '--title=FOOBAR', for example " quit -2 } name="${1}" shift base="$(/usr/bin/basename ${name})" /usr/local/bin/op-item-exists "${base}" && { print "${prog}: item already exists: ${name}" quit 1 } if [[ -n "${stdin:-}" ]] then mkdir -p "${tmp}" 1>/dev/null 2>&1 || { print - "${prog}: unable to create temp dir" quit 1 } path="${tmp}/${base}" /bin/cat - >${path} || { print - "${prog}: unable to create temp file" quit 1 } else path="${name}" fi ${op} create document ${path} "${@}" quit $?
0 -
Here is script #2 (for any items, not just documents):
op-delete-item
. It's mostly just a wrapper aroundop delete item
, but it also usesop-item-exists
to make sure that the item exists outside of the Trash, before it attempts the deletion ...#!/bin/zsh -f prog=${0##*/} op=/usr/local/bin/op [[ $# -lt 1 ]] && { print - " usage: ${prog} [ -iI ] item-name [ optional 'op delete item' arguments ] returns 0 if the item is deleted, otherwise non-zero optional arguments could contain '--vault=XXX' " exit -2 } item="${1}" shift /usr/local/bin/op-item-exists "${item}" || { print "${prog}: item not found: ${item}" exit 1 } ${op} delete item "${item}" "${@}" exit $?
0 -
Here is script #3 (just for documents):
op-update-document
. It will only perform an update if the referenced item already exists ...#!/bin/zsh -f prog=${0##*/} op=/usr/local/bin/op jq=/usr/bin/jq tmp0=/tmp/.${prog}.0.$$ tmp1=/tmp/.${prog}.1.$$ quit() { /bin/rm -rf ${tmp0} ${tmp1} exit ${1} } trap 'quit 1' 1 2 3 15 stdin= while getopts :iI o do case "${o}" in ([iI]) stdin=t ;; (*) print -r -- "${0##*/}: invalid option: -${o}" exit 1 ;; esac done shift $(( ${OPTIND} - 1 )) [[ $# -lt 1 ]] && { print - " usage: ${prog} [ -iI ] item-name [ optional 'op create document' arguments ] returns 0 if the item exists and is updated, otherwise non-zero option -i or -I means take item contents from stdin optional arguments could contain '--vault=XXX' or '--title=FOOBAR', for example " quit -2 } name="${1}" shift base="$(/usr/bin/basename ${name})" /usr/local/bin/op-item-exists "${base}" >${tmp1} || { print "${prog}: item not found: ${name}" quit 1 } if [[ -n "${stdin:-}" ]] then mkdir -p "${tmp0}" 1>/dev/null 2>&1 || { print - "${prog}: unable to create temp dir" quit 1 } path="${tmp0}/${base}" /bin/cat - >${path} || { print - "${prog}: unable to create temp file" quit 1 } else path="${name}" fi ${op} create document ${path} "${@}" rc=$? [[ ${rc} == 0 ]] && { # tmp1 contains the uuid of the original document /usr/local/bin/op-rm-item "$(/bin/cat ${tmp1})" rc=$? } quit ${rc}
0 -
That approach should work, @HippoMan. Thanks for sharing it. :)
Regarding your email you sent last week... hrmm... I'm not seeing anything here. It's possible that I'm looking wrong though, as I'm not a pro at our email system. Did you use the same email address as your forum account?
Rick
0 -
Yes, my email came from the same address that I use here. However, I'll send it again later today or tomorrow.
Also, I'm glad that you think my approach will work. I'll have some more scripts within a few days.
0 -
Note: when you see
op-item-exists
in the scripts above, it's the code that I posted here: https://discussions.agilebits.com/discussion/comment/432998/#Comment_432998This is a slightly enhanced version of
op-item-exists
...#!/bin/zsh -f prog=${0##*/} op=/usr/local/bin/op jq=/usr/bin/jq tmp0=/tmp/.${prog}.0.$$ tmp1=/tmp/.${prog}.1.$$ quit() { /bin/rm -f ${tmp0} ${tmp1} exit ${1} } trap 'quit 1' 1 2 3 15 [[ $# -lt 1 ]] && { print - " usage: ${prog} item [ optional 'op get item' arguments ] returns 0 if the item exists, otherwise non-zero optional arguments could contain '--vault=XXX', for example " quit -2 } item="${1}" shift # Use temp files so that we can catch the `op` return code # before passing any results to `jq`, and likewise for # catching the `jq` return code before checking its results. ${op} get item "${item}" "${@}" >${tmp0} || quit 1 [[ -s "${tmp0}" ]] || quit 1 filter='.uuid' ${jq} -M --raw-output "${filter}" <${tmp0} >${tmp1} || quit 1 # Count the number if items found. found=0 while read uuid do /usr/local/bin/op-uuid-exists "${uuid}" || continue (( found = found + 1 )) done <${tmp1} case "${found}" in (1) # Found one item. Return its uuid in stdout with a 0 return code /bin/cat "${tmp1}" quit 0 ;; (0) # No items found quit -1 ;; (*) # Found more than one item. # The return code is the number of items. print - "${found} matches for ${item} ${*}" quit ${found} ;; esac
0 -
That's very cool. I wish our tool made that easier though.
Rick
0