├── 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