├── Makefile ├── README.org ├── bucket.1 ├── bucket.fish ├── bucket.sh ├── buckets.jpg ├── completions └── bucket.fish └── getopts.fish /Makefile: -------------------------------------------------------------------------------- 1 | # ** Variables 2 | PREFIX := /usr/local 3 | 4 | DEST_SCRIPT := $(PREFIX)/bin/bucket 5 | DEST_FISH_COMPLETION := $(PREFIX)/share/fish/completions/bucket.fish 6 | DEST_FISH_GETOPTS := $(PREFIX)/share/fish/functions/getopts.fish 7 | DEST_MAN_PAGE := $(PREFIX)/share/man/man1/bucket.1 8 | DEST_README := $(PREFIX)/share/doc/bucket/README.org 9 | 10 | INSTALL := install -D -p 11 | INSTALL_DATA := $(INSTALL) -m 644 12 | 13 | # ** Rules 14 | 15 | # By default, install the Bash script, the Fish completions, and the man page 16 | install: install-bash install-data 17 | 18 | install-bash: install-data 19 | $(INSTALL) bucket.sh $(DEST_SCRIPT) 20 | 21 | # Install the Fish script instead of the Bash script, and install the completions and man page 22 | install-fish: install-data 23 | $(INSTALL) bucket.fish $(DEST_SCRIPT) 24 | $(INSTALL) getopts.fish $(DEST_FISH_GETOPTS) 25 | 26 | install-data: install-fish-completions install-man-page install-readme 27 | 28 | install-fish-completions: 29 | $(INSTALL) completions/bucket.fish $(DEST_FISH_COMPLETION) 30 | 31 | install-man-page: 32 | $(INSTALL_DATA) bucket.1 $(DEST_MAN_PAGE) 33 | 34 | install-readme: 35 | $(INSTALL_DATA) README.org $(DEST_README) 36 | 37 | uninstall: 38 | rm -f $(DEST_SCRIPT) $(DEST_FISH_COMPLETION) $(DEST_FISH_GETOPTS) $(DEST_MAN_PAGE) $(DEST_README) 39 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | [[buckets.jpg]] 2 | 3 | These cats all have buckets. Where's your bucket? 4 | 5 | =bucket= is like a clipboard manager for the shell. Or it's like Emacs/Vim-style registers for the shell. It makes it easy to save clips of text to named "buckets" (or a default, unnamed bucket) and access them later. 6 | 7 | ** Examples 8 | Pour into default bucket by argument: 9 | #+BEGIN_SRC sh 10 | $ bucket "I wondered why the frisbee was getting bigger, and then it hit me" 11 | #+END_SRC 12 | 13 | Pour from default bucket: 14 | #+BEGIN_SRC sh 15 | $ bucket 16 | I wondered why the frisbee was getting bigger, and then it hit me 17 | #+END_SRC 18 | 19 | Pipe default bucket into named bucket: 20 | #+BEGIN_SRC sh 21 | $ bucket | bucket jokes 22 | #+END_SRC 23 | 24 | Pipe into named bucket, verbosely: 25 | #+BEGIN_SRC sh 26 | $ dmesg | grep error | bucket -v errors 27 | [ 7.817768] EXT4-fs (sdc1): re-mounted. Opts: errors=remount-ro,user_xattr 28 | #+END_SRC 29 | 30 | Pour from named bucket: 31 | #+BEGIN_SRC sh 32 | $ bucket errors 33 | [ 7.817768] EXT4-fs (sdc1): re-mounted. Opts: errors=remount-ro,user_xattr 34 | #+END_SRC 35 | 36 | Append to a bucket by argument: 37 | #+BEGIN_SRC sh 38 | $ bucket -a jokes "A plateau is the highest form of flattery." 39 | $ bucket jokes 40 | I wondered why the frisbee was getting bigger, and then it hit me 41 | A plateau is the highest form of flattery. 42 | #+END_SRC 43 | 44 | List buckets: 45 | #+BEGIN_SRC sh 46 | $ bucket -l 47 | bucket errors jokes 48 | #+END_SRC 49 | 50 | List buckets verbosely (just the first line of each bucket): 51 | #+BEGIN_SRC sh 52 | $ bucket -lv 53 | bucket: I wondered why the frisbee was getting bigger, and then it hit me 54 | errors: [ 7.817768] EXT4-fs (sdc1): re-mounted. Opts: errors=remount-ro,user_xattr 55 | jokes: I wondered why the frisbee was getting bigger, and then it hit me 56 | #+END_SRC 57 | 58 | List buckets completely: 59 | #+BEGIN_SRC sh 60 | $ bucket -lV 61 | bucket: 62 | I wondered why the frisbee was getting bigger, and then it hit me 63 | 64 | errors: 65 | [ 7.817768] EXT4-fs (sdc1): re-mounted. Opts: errors=remount-ro,user_xattr 66 | 67 | jokes: 68 | I wondered why the frisbee was getting bigger, and then it hit me 69 | A plateau is the highest form of flattery. 70 | #+END_SRC 71 | 72 | Grep buckets: 73 | #+BEGIN_SRC sh 74 | $ bucket -g frisbee 75 | bucket:I wondered why the frisbee was getting bigger, and then it hit me 76 | jokes:I wondered why the frisbee was getting bigger, and then it hit me 77 | #+END_SRC 78 | 79 | Edit buckets (using =$EDITOR=): 80 | #+BEGIN_SRC sh 81 | $ bucket -e 82 | Waiting for Emacs... 83 | $ bucket -e jokes 84 | Waiting for Emacs... 85 | #+END_SRC 86 | 87 | Empty buckets: 88 | #+BEGIN_SRC sh 89 | $ bucket -E 90 | $ bucket 91 | $ bucket -E jokes 92 | $ bucket jokes 93 | #+END_SRC 94 | 95 | Expire old buckets: 96 | #+BEGIN_SRC sh 97 | $ sleep 14d # Default: 2 weeks 98 | $ bucket -x 99 | $ bucket -l 100 | #+END_SRC 101 | 102 | And, of course, you can always: 103 | #+BEGIN_SRC sh 104 | $ bucket <(echo ' 105 | ___ 106 | ___======____=---=) 107 | /T \_--===) 108 | [ \ (0) \~ \_-==) 109 | \ / )J~~ \-=) 110 | \\\\___/ )JJ~~~ \) 111 | \_____/JJ~~~~~ \\ 112 | / \ , \J~~~~~ \\ 113 | (-\)\=|\\\\\~~~~ L__ 114 | (\\\\) (\\\\\)_ \==__ 115 | \V \\\\\) ===_____ \\\\\\\\\\\\ 116 | \V) \_) \\\\\\\\JJ\J\) 117 | /J\JT\JJJJ) 118 | (JJJ| \UUU) 119 | (UU)') 120 | $ bucket | cat 121 | *purr* 122 | 123 | # or 124 | $ make cheezburger | bucket 125 | $ bucket | cat 126 | .-. _ __ 127 | ` )`'-,`\ .-''``.-' 128 | _.-''-. _.' `'--._.' ,-' / 129 | `\ _`'--'` .' 130 | '._ _`- .--. .--. (` 131 | `.' / '.' \ '. 132 | .' \ 0 | 0 / '. 133 | / _ '._.---._.' _ \ 134 | / `'-. ( ) .-'` \ 135 | / .---'_. . `-,-` . ._'---. \ 136 | | -'` . | . `'- | 137 | /_ . ' /;\ ' . ,_\ 138 | '-. '-..-`( ' )`-..-' / 139 | '._ '-' _.' 140 | '-..,__ __,..-' 141 | `'---'` 142 | #+END_SRC 143 | 144 | ** Usage 145 | Yes, you can haz usage. 146 | 147 | #+BEGIN_SRC 148 | Usage: 149 | command | bucket [OPTIONS] [BUCKET] 150 | bucket [OPTIONS] [BUCKET] [DATA] 151 | 152 | The first form reads data from STDIN and writes to the default bucket, 153 | or BUCKET if given. The second form reads DATA and/or BUCKET from 154 | arguments, writing to a bucket or printing a bucket's contents. 155 | 156 | Options: 157 | -a, --append Append to bucket 158 | -d, --date Sort by date 159 | -e, --edit Edit bucket 160 | -E, --empty Empty bucket 161 | -g PATTERN, --grep PATTERN Grep in buckets 162 | -h, --help i can Haz cheezburger? 163 | -l, --list List buckets 164 | -v, --verbose Verbose output 165 | -V, --VERBOSE VERY verbose output 166 | -x, --expire eXpire old buckets (default: +14 days) 167 | --directory DIRECTORY Bucket storage directory 168 | #+END_SRC 169 | 170 | ** Installation 171 | 172 | *** Automatic 173 | To install ~bucket~ automatically, run ~sudo make install~, which will install the Bash script, the Fish completions, the man page, and this =README= file. 174 | 175 | *Note:* I recommend using the fantastic =checkinstall= utility, which automatically turns any ~make install~ command (or any other command) into a Debian/RPM package and installs it using your distro's package manager. This helps you keep track of what software you have installed, and makes it easy to uninstall software. =checkinstall= should be packaged in nearly all Linux distros. 176 | 177 | If you want to install the Fish script, run ~sudo make install-fish~, which will do all of the above, except with the Fish script instead of the Bash script. 178 | 179 | *** Manual 180 | Or, you can install it manually, e.g. into your homedir: 181 | 182 | + =bucket.sh= or =bucket.fish= should go into a directory in your =PATH=. You will probably want to remove the extension or symlink it to make it easier to type. 183 | + =completions/bucket.fish= can be put in =~/.config/fish/completions=, and then Fish will provide =TAB= auto-completion for options and bucket names. 184 | + =getopts.fish= should be put in =~/.config/fish/functions= if you are using =bucket.fish=. 185 | + =bucket.1= should go in =~/.local/share/man/man1=. 186 | 187 | *** Uninstallation 188 | If you installed with ~make install~, you can uninstall =bucket= (*D:*) with ~sudo make uninstall~. 189 | 190 | ** Files 191 | + =bucket= is provided as both a Bash script, ~bucket.sh~, and a Fish script, ~bucket.fish~. They work identically (or else it's a bug!). But Fish makes more sense, because how could you put a bash in a bucket? But Bash is faster, because how fast is a goldfish, really? 192 | + =completions/bucket.fish= can be put in =~/.config/fish/completions=, and then Fish will provide =TAB= auto-completion for options and bucket names. 193 | + ~getopts.fish~ is used by the Fish version, and it's not yet part of the standard Fish distribution, so it's included here for convenience. Just drop it into =~/.config/fish/functions=. (And check it out! It works really well and is well-documented.) 194 | + =bucket.1= is the man page, man. 195 | + =Makefile= is how we make the donuts. 196 | 197 | ** Configuration 198 | + *Empty/expire commands* 199 | - The default command for =empty= and =expire= is =trash-put=, part of the =trash-cli= package,which may not be installed on your system by default. =trash-cli= is a great package, so I recommend installing it. It uses the standard XDG trash bin from the terminal. 200 | - If you prefer, you can easily change the default to =rm= by changing the =deleteCommand= variable near the top of the script. 201 | - Or you could change it to something like ~mv -t ~/.local/share/Trash/files~, although that might cause the trashed files to not show up in GUI trash apps, since they would be missing the ~.trashinfo~ files. 202 | + *Storage directory* 203 | - The default directory for bucket storage is =~/.cache/bucket=. =~/.cache= is probably excluded by most backup programs (it should be), so this seems like a good place for buckets, since buckets are intended to be temporary storage. 204 | - However, you can set a custom directory using the ~--directory~ option. Unlike the default directory, it will not be created for you if it doesn't exist. You might set this option in an alias, that way whenever you use that alias, the buckets would be stored in that directory. This could be helpful if you wanted to store a certain set of buckets more permanently, in a place where they would typically be included in backups. 205 | 206 | ** Aliases 207 | :PROPERTIES: 208 | :ID: fa457145-d56c-4674-95c1-b1f162758730 209 | :END: 210 | If you like to think in terms of registers, you might like to alias ~bucket~ to something like ~r~ so you can do: 211 | 212 | #+BEGIN_SRC sh 213 | $ alias r bucket 214 | $ rgrep funny /usr/share/games/fortunes | r jokes 215 | $ r jokes 216 | ... 217 | people:We all know that no one understands anything that is not funny. 218 | ... 219 | 220 | $ alias rl "bucket -l" 221 | $ rl 222 | jokes 223 | #+END_SRC 224 | 225 | Or you might like to use: 226 | 227 | #+BEGIN_SRC sh 228 | $ alias buckets "bucket -l" 229 | $ buckets 230 | jokes 231 | #+END_SRC 232 | 233 | If you like to keep a to-do list, you might find this handy: 234 | 235 | #+BEGIN_SRC sh 236 | $ alias rt "bucket today" 237 | $ alias rta "'bucket -a today" 238 | $ rta Walk the cat 239 | $ rt 240 | Walk the cat 241 | $ rta Feed the cat 242 | $ rt 243 | Walk the cat 244 | Feed the cat 245 | $ cat --walk --feed 246 | MEOW! 247 | *purr* 248 | $ rt -E 249 | $ rt 250 | $ 251 | #+END_SRC 252 | 253 | If you want to store buckets more permanently, like for a personal log, you might use an alias like: 254 | 255 | #+BEGIN_SRC sh 256 | $ alias log 'bucket.sh --directory ~/.log --append $(date --rfc-3339=date) $(date +%H:%M)' 257 | $ log "Gus said that he wants to get a fish, just as a pet. I'm afraid I don't trust him." 258 | $ log "Just as I suspected, I caught him ordering tartar sauce online. He'll be eating nothing but cheezburgers from now on." 259 | $ log -lV 260 | 2015-10-25: 261 | 20:26 Gus said that he wants to get a fish, just as a pet. I'm afraid I don't trust him. 262 | 21:12 Just as I suspected, I caught him ordering tartar sauce online. He'll be eating nothing but cheezburgers from now on. 263 | #+END_SRC 264 | 265 | *Note:* ~bucket.sh~ handles arguments slightly differently than ~bucket.fish~. The Bash version allows specifying options and arguments in any order, while the Fish version treats all options after the first non-option argument as non-option arguments. This means that the Bash version allows a command like ~bucket.sh --directory ~/.log --append Monday 13:37 -l -V~, and the ~-l -V~ would cause the script to list buckets verbosely; but the same command using ~bucket.fish~ would append the string ~13:37 -l -V~ to the ~Monday~ bucket. So if you use the Fish version and want to use an alias like this, you'll need to use a separate alias for listing, like: 266 | 267 | #+BEGIN_SRC sh 268 | alias logV 'bucket.fish --directory ~/.log -lV' 269 | #+END_SRC 270 | 271 | ** Development 272 | I can't think of much else to add right now. But I welcome suggestions and pull requests. Bug reports may be kept to yourself (just kidding). 273 | 274 | ** License 275 | I really don't think a license is necessary for something this straightfoward. But consider it GPL'ed. 276 | -------------------------------------------------------------------------------- /bucket.1: -------------------------------------------------------------------------------- 1 | .TH BUCKET 1 2 | .SH NAME 3 | bucket \- a clipboard manager or registers for the shell 4 | .SH SYNOPSIS 5 | command | \fBbucket\fR [\fIOPTIONS\fR] [\fIBUCKET\fR] 6 | .PP 7 | \fBbucket\fR [\fIOPTIONS\fR] [\fIBUCKET\fR] [\fIDATA\fR] 8 | .PP 9 | The first form reads data from \fISTDIN\fR and writes to the default bucket, or \fIBUCKET\fR if given. 10 | The second form reads \fIDATA\fR and/or \fIBUCKET\fR from arguments, writing to a bucket or printing a bucket's contents. 11 | .SH DESCRIPTION 12 | \fBbucket\fR is like Emacs/Vim-style registers for the shell. 13 | It makes it easy to save clips of text to named "buckets" (or a default, unnamed bucket) and access them later. 14 | .SH OPTIONS 15 | .TP 16 | \fB\-a\fR, \fB\-\-append\fR 17 | Append to bucket 18 | .TP 19 | \fB\-d\fR, \fB\-\-date\fR 20 | Sort by date 21 | .TP 22 | \fB\-e\fR, \fB\-\-edit\fR 23 | Edit bucket 24 | .TP 25 | \fB\-E\fR, \fB\-\-empty\fR 26 | Empty bucket 27 | .TP 28 | \fB\-g\fR \fIPATTERN\fR, \fB\-\-grep\fR \fIPATTERN\fR 29 | Grep in buckets 30 | .TP 31 | \fB\-h\fR, \fB\-\-help\fR 32 | i can Haz cheezburger? 33 | .TP 34 | \fB\-l\fR, \fB\-\-list\fR 35 | List buckets 36 | .TP 37 | \fB\-v\fR, \fB\-\-verbose\fR 38 | Verbose output 39 | .TP 40 | \fB\-V\fR, \fB\-\-VERBOSE\fR 41 | VERY verbose output 42 | .TP 43 | \fB\-x\fR, \fB\-\-expire\fR 44 | eXpire old buckets (default: +14 days) 45 | .TP 46 | \fB\-\-directory\fR \fIDIRECTORY\fR 47 | Bucket storage directory 48 | .SH EXAMPLES 49 | Pour into default bucket by argument: 50 | .PP 51 | .nf 52 | .RS 53 | $ \fBbucket\fR "I wondered why the frisbee was getting bigger, and then it hit me" 54 | .RE 55 | .fi 56 | .PP 57 | Pour from default bucket: 58 | .PP 59 | .nf 60 | .RS 61 | $ \fBbucket\fR 62 | I wondered why the frisbee was getting bigger, and then it hit me 63 | .RE 64 | .fi 65 | .PP 66 | Pipe default bucket into named bucket: 67 | .PP 68 | .nf 69 | .RS 70 | $ \fBbucket\fR | \fBbucket\fR jokes 71 | .RE 72 | .fi 73 | .PP 74 | Pipe into named bucket, verbosely: 75 | .PP 76 | .nf 77 | .RS 78 | $ dmesg | grep error | \fBbucket\fR -v errors 79 | [ 7.817768] EXT4-fs (sdc1): re-mounted. Opts: errors=remount-ro,user_xattr 80 | .RE 81 | .fi 82 | .PP 83 | Pour from named bucket: 84 | .PP 85 | .nf 86 | .RS 87 | $ \fBbucket\fR errors 88 | [ 7.817768] EXT4-fs (sdc1): re-mounted. Opts: errors=remount-ro,user_xattr 89 | .RE 90 | .fi 91 | .PP 92 | Append to a bucket by argument: 93 | .PP 94 | .nf 95 | .RS 96 | $ \fBbucket\fR -a jokes "A plateau is the highest form of flattery." 97 | $ \fBbucket\fR jokes 98 | I wondered why the frisbee was getting bigger, and then it hit me 99 | A plateau is the highest form of flattery. 100 | .RE 101 | .fi 102 | .PP 103 | List buckets: 104 | .PP 105 | .nf 106 | .RS 107 | $ \fBbucket\fR -l 108 | bucket errors jokes 109 | .RE 110 | .fi 111 | .PP 112 | List buckets verbosely (just the first line of each bucket): 113 | .PP 114 | .nf 115 | .RS 116 | $ \fBbucket\fR -lv 117 | bucket: I wondered why the frisbee was getting bigger, and then it hit me 118 | errors: [ 7.817768] EXT4-fs (sdc1): re-mounted. Opts: errors=remount-ro,user_xattr 119 | jokes: I wondered why the frisbee was getting bigger, and then it hit me 120 | .RE 121 | .fi 122 | .PP 123 | List buckets completely: 124 | .PP 125 | .nf 126 | .RS 127 | $ \fBbucket\fR -lV 128 | bucket: 129 | I wondered why the frisbee was getting bigger, and then it hit me 130 | errors: 131 | [ 7.817768] EXT4-fs (sdc1): re-mounted. Opts: errors=remount-ro,user_xattr 132 | jokes: 133 | I wondered why the frisbee was getting bigger, and then it hit me 134 | A plateau is the highest form of flattery. 135 | .RE 136 | .fi 137 | .PP 138 | Grep buckets: 139 | .PP 140 | .nf 141 | .RS 142 | $ \fBbucket\fR -g frisbee 143 | bucket:I wondered why the frisbee was getting bigger, and then it hit me 144 | jokes:I wondered why the frisbee was getting bigger, and then it hit me 145 | .RE 146 | .fi 147 | .PP 148 | Edit buckets (using \fI$EDITOR\fR): 149 | .PP 150 | .nf 151 | .RS 152 | $ \fBbucket\fR -e 153 | Waiting for Emacs... 154 | $ \fBbucket\fR -e jokes 155 | Waiting for Emacs... 156 | .RE 157 | .fi 158 | .PP 159 | Empty buckets: 160 | .PP 161 | .nf 162 | .RS 163 | $ \fBbucket\fR -E 164 | $ \fBbucket\fR 165 | $ \fBbucket\fR -E jokes 166 | $ \fBbucket\fR jokes 167 | .RE 168 | .fi 169 | .PP 170 | Expire old buckets: 171 | .PP 172 | .nf 173 | .RS 174 | $ sleep 14d # Default: 2 weeks 175 | $ \fBbucket\fR -x 176 | $ \fBbucket\fR -l 177 | .RE 178 | .fi 179 | .SH CONFIGURATION 180 | .B 181 | Empty/expire commands 182 | .RS 183 | The default command for \fI\-\-empty\fR and \fI\-\-expire\fR is \fItrash-put\fR, part of the \fItrash-cli\fR package,which may not be installed on your system by default. 184 | \fItrash-cli\fR is a great package, so I recommend installing it. It uses the standard XDG trash bin from the terminal. 185 | .PP 186 | If you prefer, you can easily change the default to \fIrm\fR by changing the \fIdeleteCommand\fR variable near the top of the script. 187 | .PP 188 | Or you could change it to something like \fImv -t ~/.local/share/Trash/files\fR, although that might cause the trashed files to not show up in GUI trash apps, since they would be missing the \fI.trashinfo\fR files. 189 | .PP 190 | .RE 191 | .B 192 | Storage directory 193 | .RS 194 | The default directory for bucket storage is \fI~/.cache/bucket\fR. 195 | \fI~/.cache\fR is probably excluded by most backup programs (it should be), so this seems like a good place for buckets, since buckets are intended to be temporary storage. 196 | .PP 197 | However, you can set a custom directory using the \fI\-\-directory\fR option. 198 | Unlike the default directory, it will not be created for you if it doesn't exist. 199 | You might set this option in an alias, that way whenever you use that alias, the buckets would be stored in that directory. 200 | This could be helpful if you wanted to store a certain set of buckets more permanently, in a place where they would typically be included in backups. 201 | .SH SEE ALSO 202 | For more amusing info, see the readme file at \fI/usr/{local,}/share/doc/bucket/README.org\fR 203 | .SH DEVELOPMENT 204 | I can't think of much else to add right now. 205 | But I welcome suggestions and pull requests. 206 | Bug reports may be kept to yourself (just kidding). 207 | .PP 208 | \fBbucket\fR's home is at \fIhttps://github.com/alphapapa/bucket\fR. 209 | .SH LICENSE 210 | I really don't think a license is necessary for something this straightfoward. 211 | But consider it GPL'ed. 212 | -------------------------------------------------------------------------------- /bucket.fish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env fish 2 | 3 | # * bucket.fish 4 | 5 | # ** Defaults 6 | set bucket bucket # Default bucket name 7 | set dir ~/.cache/bucket 8 | set deleteCommand trash-put 9 | set expireDays 14 10 | 11 | 12 | # ** Functions 13 | function debug --description "Print debug message in yellow" 14 | # Do NOT use separate set_color commands, or else you risk 15 | # polluting later command substitutions! See 16 | # https://github.com/fish-shell/fish-shell/issues/2378 17 | isset debug 18 | and echo (set_color yellow)"DEBUG: $argv"(set_color normal) >&2 19 | #and echo "DEBUG: $argv" >&2 20 | end 21 | function die --description "Print error message and quit" 22 | echo (set_color red)"$argv"(set_color normal) >&2 23 | #echo "$argv" >&2 24 | exit 1 25 | end 26 | function verbose --description "Print message if verbose" 27 | isset verbose 28 | or isset reallyVerbose 29 | and echo "$argv" >&2 30 | end 31 | function isset --description "Test if variables named by args are set" 32 | # "set -q" everywhere gets old 33 | set -q $argv 34 | end 35 | function usage 36 | echo "Usage:" 37 | echo " command | bucket [OPTIONS] [BUCKET]" 38 | echo " bucket [OPTIONS] [BUCKET] [DATA]" 39 | echo 40 | 41 | echo "The first form reads data from STDIN and writes to the default bucket, 42 | or BUCKET if given. The second form reads DATA and/or BUCKET from 43 | arguments, writing to a bucket or printing a bucket's contents." 44 | 45 | echo 46 | echo "Options:" 47 | echo " -a, --append Append to bucket" 48 | echo " -d, --date Sort by date" 49 | echo " -e, --edit Edit bucket" 50 | echo " -E, --empty Empty bucket" 51 | echo " -g PATTERN, --grep PATTERN Grep in buckets" 52 | echo " -h, --help i can Haz cheezburger?" 53 | echo " -l, --list List buckets" 54 | echo " -v, --verbose Verbose output" 55 | echo " -V, --VERBOSE VERY verbose output" 56 | echo " -x, --expire eXpire old buckets (default: +$expireDays days)" 57 | echo " --directory DIRECTORY Bucket storage directory" 58 | end 59 | 60 | 61 | # ** Args 62 | while set optarg (getopts "a:append d:date D:debug e:edit E:empty g:grep h:help l:list v:verbose V:VERBOSE x:expire directory:" $argv) 63 | switch $optarg[1] 64 | case a 65 | set append true 66 | case d 67 | set sort "-tr" 68 | case D 69 | set debug true 70 | debug "Debugging on" 71 | case e 72 | set edit true 73 | case E 74 | set empty true 75 | case g 76 | set -e optarg[1] 77 | set grep "$optarg" # Flatten list 78 | break 79 | case h 80 | usage 81 | exit 82 | case l 83 | set list true 84 | case v 85 | set verbose -v 86 | case V 87 | set reallyVerbose true 88 | case x 89 | set expire 90 | case directory 91 | set customDir $optarg[2] 92 | debug "Using custom directory: $customDir" 93 | case \* 94 | usage 95 | echo 96 | die "Unknown option: $optarg[1]" 97 | end 98 | end 99 | 100 | set args $optarg 101 | set numargs (count $args) 102 | 103 | # *** Check STDIN and bucket name 104 | if not isatty stdin 105 | debug "Data from STDIN" 106 | 107 | set stdin true 108 | 109 | if test $numargs -eq 0 110 | debug "No args; using default bucket" 111 | 112 | else if test $numargs -eq 1 113 | debug "One arg; using bucket: $args" 114 | 115 | set bucket $args 116 | 117 | else 118 | debug "Multiple args; using bucket $args[1]" 119 | verbose "Ignoring extra arguments" 120 | 121 | set bucket $args[1] 122 | end 123 | else 124 | debug "No data from STDIN" 125 | 126 | if test $numargs -eq 0 127 | debug "No args" 128 | 129 | else if test $numargs -eq 1 130 | if test -f $dir/$args 131 | # Bucket exists with name; pour it 132 | set bucket $args 133 | 134 | debug "Using bucket $bucket; no data remaining in args" 135 | else 136 | # No such bucket; use args as data for default bucket 137 | set data $args 138 | 139 | debug "No bucket named '$args'; using args as data for default bucket" 140 | end 141 | 142 | else 143 | # Multiple args 144 | set bucket $args[1] 145 | set -e args[1] 146 | set data $args 147 | 148 | debug "Using bucket $bucket; using $numargs remaining args as data" 149 | end 150 | end 151 | 152 | # **** Sanitize bucket name (since it's passed to eval and trash-put/rm) 153 | set bucket (echo $bucket | sed -r 's/[~.]//g') 154 | 155 | # *** Check for conflicting args 156 | if begin; isset empty; and isset expire; end 157 | set conflicting true 158 | end 159 | 160 | if isset conflicting 161 | die "Conflicting operations given." 162 | end 163 | 164 | 165 | # ** Check directory 166 | if isset customDir 167 | # Custom bucket directory; don't make it 168 | test -d $customDir 169 | or die "Directory doesn't exist: $customDir" 170 | 171 | set dir $customDir 172 | 173 | else 174 | # Standard bucket directory; make it if necessary 175 | if not test -d $dir 176 | # Make dir 177 | mkdir -p $dir 178 | or die "Unable to make bucket directory: $dir" 179 | end 180 | end 181 | 182 | # cd just to be extra safe 183 | cd $dir 184 | or die "Unable to enter bucket directory: $dir" 185 | 186 | 187 | # ** Main 188 | if isset list 189 | # *** List buckets 190 | debug "Listing buckets" 191 | 192 | if isset verbose 193 | for file in (ls $sort) 194 | echo -e (set_color blue)$file(set_color normal)": " (head -n1 $file) 195 | #echo "$file:" (head -n1 $file) 196 | end 197 | else if isset reallyVerbose 198 | for file in (ls $sort) 199 | echo -e (set_color blue)$file(set_color normal)":" 200 | #echo "$file:" 201 | cat $file 202 | echo 203 | end 204 | else 205 | ls 206 | end 207 | 208 | else if isset grep 209 | # *** Grep buckets 210 | debug "Grepping buckets" 211 | 212 | grep -i $grep * 213 | 214 | else if isset edit 215 | # *** Edit bucket 216 | debug "Editing bucket" 217 | 218 | eval "$EDITOR \"$dir/$bucket\"" 219 | 220 | else if isset empty 221 | # *** Empty bucket 222 | debug "Emptying bucket $bucket" 223 | 224 | if not test -f "$dir/$bucket" 225 | verbose "No such bucket" 226 | else 227 | eval "$deleteCommand $verbose \"$dir/$bucket\"" 228 | end 229 | 230 | else if isset expire 231 | # *** Expire buckets 232 | debug "Expiring buckets" 233 | 234 | find $dir -type f -mtime +$expireDays -exec $deleteCommand $verbose '{}' + 235 | 236 | else 237 | if begin; not isset stdin; and not isset data; end 238 | # *** Pour bucket 239 | debug "Pouring bucket..." 240 | 241 | if not test -r "$dir/$bucket" 242 | verbose "No such bucket" 243 | else if not test -s "$dir/$bucket" 244 | verbose "Bucket is empty" 245 | else 246 | cat "$dir/$bucket" 247 | end 248 | 249 | else 250 | # *** Fill bucket 251 | debug "Filling bucket..." 252 | 253 | if isset stdin 254 | debug "Catting STDIN" 255 | 256 | if isset append 257 | cat >>"$dir/$bucket" 258 | else 259 | cat >"$dir/$bucket" 260 | end 261 | 262 | else 263 | debug "Echoing data" 264 | 265 | if isset append 266 | echo $data >>"$dir/$bucket" 267 | else 268 | echo $data >"$dir/$bucket" 269 | end 270 | end 271 | 272 | # **** Display bucket if verbose 273 | isset verbose 274 | or isset reallyVerbose 275 | and cat "$dir/$bucket" 276 | end 277 | end 278 | -------------------------------------------------------------------------------- /bucket.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # * bucket.sh 4 | 5 | # ** Defaults 6 | bucket=bucket # Default bucket name 7 | dir=~/.cache/bucket 8 | deleteCommand=trash-put 9 | expireDays=14 10 | 11 | 12 | # ** Functions 13 | function debug { 14 | [[ $debug ]] && echo "DEBUG: $@" >&2 15 | } 16 | function die { 17 | echo "$@" >&2 18 | exit 1 19 | } 20 | function verbose { 21 | [[ $verbose ]] && echo "$@" >&2 22 | } 23 | function usage { 24 | echo "Usage:" 25 | echo " command | bucket [OPTIONS] [BUCKET]" 26 | echo " bucket [OPTIONS] [BUCKET] [DATA]" 27 | echo 28 | 29 | echo "The first form reads data from STDIN and writes to the default bucket, 30 | or BUCKET if given. The second form reads DATA and/or BUCKET from 31 | arguments, writing to a bucket or printing a bucket's contents." 32 | 33 | echo 34 | echo "Options:" 35 | echo " -a, --append Append to bucket" 36 | echo " -d, --date Sort by date" 37 | echo " -e, --edit Edit bucket" 38 | echo " -E, --empty Empty bucket" 39 | echo " -g PATTERN, --grep PATTERN Grep in buckets" 40 | echo " -h, --help i can Haz cheezburger?" 41 | echo " -l, --list List buckets" 42 | echo " -v, --verbose Verbose output" 43 | echo " -V, --VERBOSE VERY verbose output" 44 | echo " -x, --expire eXpire old buckets (default: +$expireDays days)" 45 | echo " --directory DIRECTORY Bucket storage directory" 46 | } 47 | 48 | 49 | # ** Args 50 | args=$(getopt -o adDeEghlvVx -l "append,date,debug,edit,empty,grep,help,list,verbose,VERBOSE,expire,directory:" -n "bucket" -- "$@") 51 | [[ $? -eq 0 ]] || exit 1 52 | 53 | eval set -- "$args" 54 | 55 | while true 56 | do 57 | case "$1" in 58 | -a|--append) 59 | append=true ;; 60 | -d|--date) 61 | sort=-tr ;; 62 | -D|--debug) 63 | debug=true ;; 64 | -e|--edit) 65 | edit=true ;; 66 | -E|--empty) 67 | empty=true ;; 68 | -g|--grep) 69 | shift 70 | grep="$@" 71 | break ;; 72 | -h|--help) 73 | usage 74 | exit ;; 75 | -l|--list) 76 | list=true ;; 77 | -v|--verbose) 78 | verbose=-v ;; 79 | -V|--VERBOSE) 80 | verbose=-v 81 | reallyVerbose=true ;; 82 | -x|--expire) 83 | expire=true ;; 84 | --directory) 85 | shift 86 | customDir="$1" 87 | ;; 88 | --) 89 | # Remaining args 90 | shift 91 | args=("$@") 92 | numargs=${#args[@]} 93 | break ;; 94 | esac 95 | 96 | shift 97 | done 98 | 99 | # *** Check STDIN and bucket name 100 | if ! [[ -t 0 ]] 101 | then 102 | debug "Data from STDIN" 103 | 104 | stdin=true 105 | 106 | if [[ $numargs -eq 0 ]] 107 | then 108 | debug "No args; using default bucket" 109 | 110 | elif [[ $numargs -eq 1 ]] 111 | then 112 | debug "One arg; using bucket: $optarg" 113 | 114 | bucket=$args 115 | 116 | else 117 | debug "Multiple args; using bucket $optarg[1]" 118 | verbose "Ignoring extra arguments" 119 | 120 | bucket="${args[0]}" 121 | fi 122 | else 123 | debug "No data from STDIN" 124 | 125 | if [[ $numargs -eq 0 ]] 126 | then 127 | debug "No args" 128 | 129 | elif [[ $numargs -eq 1 ]] 130 | then 131 | if [[ -f $dir/$args ]] 132 | then 133 | # Bucket exists with name; pour it 134 | bucket=$args 135 | 136 | debug "Using bucket $bucket; no data remaining in args" 137 | else 138 | # No such bucket; use args as data for default bucket 139 | data=$args 140 | 141 | debug "No bucket named '$args'; using args as data for default bucket" 142 | fi 143 | 144 | else 145 | # Multiple args 146 | bucket="${args[0]}" 147 | unset args[0] 148 | data="${args[@]}" 149 | 150 | debug "Using bucket $bucket; using $numargs remaining args as data" 151 | fi 152 | fi 153 | 154 | # **** Sanitize bucket name (since it's passed to eval and trash-put/rm) 155 | bucket=$(echo "$bucket" | sed -r 's/[~.]//g') 156 | 157 | # *** Check for conflicting args 158 | if [[ $empty && $expire ]] 159 | then 160 | conflicting=true 161 | fi 162 | if [[ $conflicting ]] 163 | then 164 | die "Conflicting operations given." 165 | fi 166 | 167 | # ** Check directory 168 | if [[ $customDir ]] 169 | then 170 | # Custom bucket directory; don't make it 171 | [[ -d $customDir ]] || die "Directory doesn't exist: $customDir" 172 | 173 | dir=$customDir 174 | 175 | else 176 | # Standard bucket directory; make it if necessary 177 | if ! [[ -d $dir ]] 178 | then 179 | # Make dir 180 | mkdir -p "$dir" || die "Unable to make bucket directory: $dir" 181 | fi 182 | fi 183 | 184 | # cd just to be extra safe 185 | cd "$dir" || die "Unable to enter bucket directory: $dir" 186 | 187 | 188 | # ** Main 189 | if [[ $list ]] 190 | then 191 | # *** List buckets 192 | readarray -t files <<<"$(ls $sort)" 193 | 194 | if [[ $verbose && ! $reallyVerbose ]] 195 | then 196 | for file in "${files[@]}" 197 | do 198 | echo "$file: $(head -n1 "$file")" 199 | done 200 | elif [[ $reallyVerbose ]] 201 | then 202 | for file in "${files[@]}" 203 | do 204 | echo "$file:" 205 | cat "$file" 206 | echo 207 | done 208 | else 209 | ls 210 | fi 211 | 212 | elif [[ $grep ]] 213 | then 214 | grep -i "$grep" ./* 215 | 216 | elif [[ $edit ]] 217 | then 218 | # *** Edit bucket 219 | eval "$EDITOR \"$dir/$bucket\"" 220 | 221 | elif [[ $empty ]] 222 | then 223 | # *** Empty bucket 224 | debug "Emptying bucket..." 225 | 226 | if ! [[ -f $dir/$bucket ]] 227 | then 228 | verbose "No such bucket" 229 | else 230 | eval "$deleteCommand $verbose \"$dir/$bucket\"" 231 | fi 232 | 233 | elif [[ $expire ]] 234 | then 235 | # *** Expire buckets 236 | find "$dir" -type f -mtime +$expireDays -exec "$deleteCommand $verbose" '{}' + 237 | 238 | else 239 | if ! [[ $stdin || $data ]] 240 | then 241 | # *** Pour bucket 242 | debug "Pouring bucket..." 243 | 244 | if ! [[ -r $dir/$bucket ]] 245 | then 246 | verbose "No such bucket" 247 | elif ! [[ -s $dir/$bucket ]] 248 | then 249 | verbose "Bucket is empty" 250 | else 251 | cat "$dir/$bucket" 252 | fi 253 | 254 | else 255 | # *** Fill bucket 256 | debug "Filling bucket..." 257 | 258 | if [[ $stdin ]] 259 | then 260 | debug "Catting STDIN into bucket" 261 | 262 | if [[ $append ]] 263 | then 264 | cat >>"$dir/$bucket" 265 | else 266 | cat >"$dir/$bucket" 267 | fi 268 | 269 | else 270 | debug "Echoing data into bucket" 271 | 272 | if [[ $append ]] 273 | then 274 | echo "$data" >>"$dir/$bucket" 275 | else 276 | echo "$data" >"$dir/$bucket" 277 | fi 278 | 279 | fi 280 | 281 | # Display bucket if verbose 282 | [[ $verbose ]] && cat "$dir/$bucket" 283 | fi 284 | fi 285 | -------------------------------------------------------------------------------- /buckets.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphapapa/bucket/343456e8cbd0908fd1b39232602df5bfd1f4021e/buckets.jpg -------------------------------------------------------------------------------- /completions/bucket.fish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env fish 2 | 3 | complete -c bucket -Af -s a -l append 4 | complete -c bucket -Af -s e -l edit 5 | complete -c bucket -Af -s E -l empty 6 | complete -c bucket -Af -s g -l grep 7 | complete -c bucket -Af -s h -l help 8 | complete -c bucket -Af -s l -l list 9 | complete -c bucket -Af -s v -l verbose 10 | complete -c bucket -Af -s V -l VERBOSE 11 | complete -c bucket -Af -s x -l expire 12 | complete -c bucket -f -a '(ls ~/.cache/bucket)' 13 | -------------------------------------------------------------------------------- /getopts.fish: -------------------------------------------------------------------------------- 1 | # NAME 2 | # getopts -- getopts for fish 3 | # 4 | # SYNOPSIS 5 | # getopts