12 <p> |
12 <p> |
13 All examples were tested in <a href="https://www.gnu.org/software/bash/">GNU Bash</a>. |
13 All examples were tested in <a href="https://www.gnu.org/software/bash/">GNU Bash</a>. |
14 But they should also work in other shells. |
14 But they should also work in other shells. |
15 </p> |
15 </p> |
16 |
16 |
17 <h2>relpipe-in-cli: Hello Wordl!</h2> |
17 <m:skript jazyk="bash" výstup="xhtml"><![CDATA[ |
18 |
18 echo "<ul>"; |
19 <p> |
19 DIR=$(dirname "$XWG_STRANKA_SOUBOR"); |
20 Let's start with an obligatory Hello World example. |
20 DIR="$DIR/../vstup" |
21 </p> |
21 cd "$DIR"; |
22 |
22 # TODO: use XQuery? (but Grep and Bash are everywhere) |
23 <m:pre jazyk="bash"><![CDATA[relpipe-in-cli generate "relation_from_cli" 3 \ |
23 for f in examples-*.xml; do |
24 "a" "integer" \ |
24 grep -oP '(?<=<m:pořadí-příkladu>).*(?=</m:pořadí-příkladu>)' $f | tr \\n ' ' |
25 "b" "string" \ |
25 echo "<li><m:a href=\"${f//\.xml/}\">$(grep -oP '(?<=<nadpis>).*(?=</nadpis>)' $f)</m:a> – $(grep -oP '(?<=<perex>).+(?=</perex>)' $f)</li>"; |
26 "c" "boolean" \ |
26 done | sort | sed -E 's/^[0-9]+ //' |
27 "1" "Hello" "true" \ |
27 echo "</ul>"; |
28 "2" "World!" "false"]]></m:pre> |
28 ]]></m:skript> |
29 |
|
30 <p> |
|
31 This command generates relational data. |
|
32 In order to see them, we need to convert them to some other format. |
|
33 For now, we will use the "tabular" format and pipe relational data to the <code>relpipe-out-tabular</code>. |
|
34 </p> |
|
35 |
|
36 <m:pre jazyk="bash"><![CDATA[relpipe-in-cli generate "relation_from_cli" 3 \ |
|
37 "a" "integer" \ |
|
38 "b" "string" \ |
|
39 "c" "boolean" \ |
|
40 "1" "Hello" "true" \ |
|
41 "2" "World!" "false" \ |
|
42 | relpipe-out-tabular]]></m:pre> |
|
43 |
|
44 <p>Output:</p> |
|
45 |
|
46 <pre><![CDATA[relation_from_cli: |
|
47 ╭─────────────┬────────────┬─────────────╮ |
|
48 │ a (integer) │ b (string) │ c (boolean) │ |
|
49 ├─────────────┼────────────┼─────────────┤ |
|
50 │ 1 │ Hello │ true │ |
|
51 │ 2 │ World! │ false │ |
|
52 ╰─────────────┴────────────┴─────────────╯ |
|
53 Record count: 2 |
|
54 ]]></pre> |
|
55 |
|
56 <p> |
|
57 The syntax is simple as we see above. We specify the name of the relation, number of attributes, |
|
58 and then their definitions (names and types), |
|
59 followed by the data. |
|
60 </p> |
|
61 |
|
62 <p> |
|
63 A single stream may contain multiple relations: |
|
64 </p> |
|
65 |
|
66 <m:pre jazyk="bash"><![CDATA[(relpipe-in-cli generate a 1 x string hello; \ |
|
67 relpipe-in-cli generate b 1 y string world) \ |
|
68 | relpipe-out-tabular]]></m:pre> |
|
69 |
|
70 <p> |
|
71 Thus we can combine various commands or files and pass the result to a single relational output filter (<code>relpipe-out-tabular</code> in this case) and get: |
|
72 </p> |
|
73 |
|
74 <pre><![CDATA[a: |
|
75 ╭────────────╮ |
|
76 │ x (string) │ |
|
77 ├────────────┤ |
|
78 │ hello │ |
|
79 ╰────────────╯ |
|
80 Record count: 1 |
|
81 b: |
|
82 ╭────────────╮ |
|
83 │ y (string) │ |
|
84 ├────────────┤ |
|
85 │ world │ |
|
86 ╰────────────╯ |
|
87 Record count: 1]]></pre> |
|
88 |
|
89 <h2>relpipe-in-cli: STDIN</h2> |
|
90 |
|
91 <p> |
|
92 The number of <abbr title="Command-line interface">CLI</abbr> arguments is limited and they are passed at once to the process. |
|
93 So there is option to pass the values from STDIN instead of CLI arguments. |
|
94 Values on STDIN are expected to be separated by the null-byte. |
|
95 We can generate such data e.g. using <code>echo</code> and <code>tr</code> (or using <code>printf</code> or other commands): |
|
96 </p> |
|
97 |
|
98 <m:pre jazyk="bash"><![CDATA[echo -e "1\nHello\ntrue\n2\nWorld\nfalse" \ |
|
99 | tr \\n \\0 \ |
|
100 | relpipe-in-cli generate-from-stdin relation_from_stdin 3 \ |
|
101 a integer \ |
|
102 b string \ |
|
103 c boolean \ |
|
104 | relpipe-out-tabular]]></m:pre> |
|
105 |
|
106 <p> |
|
107 The output is same as above. |
|
108 We can use this approach to convert various formats to relational data. |
|
109 There are lot of data already in the form of null-separated values e.g. the process arguments: |
|
110 </p> |
|
111 |
|
112 <m:pre jazyk="bash"><![CDATA[cat /proc/$(pidof mc)/cmdline \ |
|
113 | relpipe-in-cli generate-from-stdin mc_args 1 a string \ |
|
114 | relpipe-out-tabular |
|
115 ]]></m:pre> |
|
116 |
|
117 <p>If we have <code>mc /etc/ /tmp/</code> running in some other terminal, the output will be:</p> |
|
118 |
|
119 <pre><![CDATA[mc_args: |
|
120 ╭────────────╮ |
|
121 │ a (string) │ |
|
122 ├────────────┤ |
|
123 │ mc │ |
|
124 │ /etc/ │ |
|
125 │ /tmp/ │ |
|
126 ╰────────────╯ |
|
127 Record count: 3]]></pre> |
|
128 |
|
129 <p> |
|
130 Also the <code>find</code> command can produce data separated by the null-byte: |
|
131 </p> |
|
132 |
|
133 <m:pre jazyk="bash"><![CDATA[find /etc/ -name '*ssh*_*' -print0 \ |
|
134 | relpipe-in-cli generate-from-stdin files 1 file_name string \ |
|
135 | relpipe-out-tabular]]></m:pre> |
|
136 |
|
137 <p>Will display something like this:</p> |
|
138 |
|
139 <pre><![CDATA[files: |
|
140 ╭───────────────────────────────────╮ |
|
141 │ file_name (string) │ |
|
142 ├───────────────────────────────────┤ |
|
143 │ /etc/ssh/ssh_host_ecdsa_key │ |
|
144 │ /etc/ssh/sshd_config │ |
|
145 │ /etc/ssh/ssh_host_ed25519_key.pub │ |
|
146 │ /etc/ssh/ssh_host_ecdsa_key.pub │ |
|
147 │ /etc/ssh/ssh_host_rsa_key │ |
|
148 │ /etc/ssh/ssh_config │ |
|
149 │ /etc/ssh/ssh_host_ed25519_key │ |
|
150 │ /etc/ssh/ssh_import_id │ |
|
151 │ /etc/ssh/ssh_host_rsa_key.pub │ |
|
152 ╰───────────────────────────────────╯ |
|
153 Record count: 9]]></pre> |
|
154 |
|
155 |
|
156 <h2>relpipe-in-fstab</h2> |
|
157 |
|
158 <p> |
|
159 Using command <code>relpipe-in-fstab</code> we can convert the <code>/etc/fstab</code> or <code>/etc/mtab</code> to relational data |
|
160 </p> |
|
161 |
|
162 <m:pre jazyk="bash"><![CDATA[relpipe-in-fstab | relpipe-out-tabular]]></m:pre> |
|
163 |
|
164 <p> |
|
165 and see them as a nice table: |
|
166 </p> |
|
167 |
|
168 <pre><![CDATA[fstab: |
|
169 ╭─────────────────┬──────────────────────────────────────┬──────────────────────┬───────────────┬───────────────────────────────────────┬────────────────┬────────────────╮ |
|
170 │ scheme (string) │ device (string) │ mount_point (string) │ type (string) │ options (string) │ dump (integer) │ pass (integer) │ |
|
171 ├─────────────────┼──────────────────────────────────────┼──────────────────────┼───────────────┼───────────────────────────────────────┼────────────────┼────────────────┤ |
|
172 │ UUID │ 29758270-fd25-4a6c-a7bb-9a18302816af │ / │ ext4 │ relatime,user_xattr,errors=remount-ro │ 0 │ 1 │ |
|
173 │ │ /dev/sr0 │ /media/cdrom0 │ udf,iso9660 │ user,noauto │ 0 │ 0 │ |
|
174 │ │ /dev/sde │ /mnt/data │ ext4 │ relatime,user_xattr,errors=remount-ro │ 0 │ 2 │ |
|
175 │ UUID │ a2b5f230-a795-4f6f-a39b-9b57686c86d5 │ /home │ btrfs │ relatime │ 0 │ 2 │ |
|
176 │ │ /dev/mapper/sdf_crypt │ /mnt/private │ xfs │ relatime │ 0 │ 2 │ |
|
177 ╰─────────────────┴──────────────────────────────────────┴──────────────────────┴───────────────┴───────────────────────────────────────┴────────────────┴────────────────╯ |
|
178 Record count: 5]]></pre> |
|
179 |
|
180 <p>And we can do the same also with a remote <code>fstab</code> or <code>mtab</code>; just by adding <code>ssh</code> to the pipeline:</p> |
|
181 |
|
182 <m:pre jazyk="bash"><![CDATA[ssh example.com cat /etc/mtab | relpipe-in-fstab | relpipe-out-tabular]]></m:pre> |
|
183 |
|
184 <p> |
|
185 The <code>cat</code> runs remotely. The <code>relpipe-in-fstab</code> and <code>relpipe-out-tabular</code> run on our machine. |
|
186 </p> |
|
187 |
|
188 <p> |
|
189 n.b. the <code>relpipe-in-fstab</code> reads the <code>/etc/fstab</code> if executed on TTY. Otherwise, it reads the STDIN. |
|
190 </p> |
|
191 |
|
192 <h2>relpipe-out-xml</h2> |
|
193 |
|
194 <p> |
|
195 Relational data can be converted to various formats and one of them is the XML. |
|
196 This is a good option for further processing e.g. using XSLT transformation or passing the XML data to some other tool. |
|
197 Just use <code>relpipe-out-xml</code> instead of <code>relpipe-out-tabular</code> and the rest of the pipeline remains unchanged: |
|
198 </p> |
|
199 |
|
200 <m:pre jazyk="bash"><![CDATA[ssh example.com cat /etc/mtab | relpipe-in-fstab | relpipe-out-xml]]></m:pre> |
|
201 |
|
202 <p> |
|
203 Will produce XML like this: |
|
204 </p> |
|
205 |
|
206 <m:pre jazyk="xml" src="examples/relpipe-out-fstab.xml"/> |
|
207 |
|
208 <p> |
|
209 Thanks to XSLT, this XML can be easily converted e.g. to an XHTML table (<code>table|tr|td</code>) or other format. |
|
210 Someone can convert such data to a (La)TeX table. |
|
211 </p> |
|
212 |
|
213 <p> |
|
214 n.b. the format is not final and will change i future versions (XML namespace, more metadata etc.). |
|
215 </p> |
|
216 |
|
217 |
|
218 <h2>relpipe-tr-validator</h2> |
|
219 |
|
220 <p> |
|
221 Just a passthrough command, so these pipelines should produce the same hash: |
|
222 </p> |
|
223 |
|
224 <m:pre jazyk="bash"><![CDATA[ |
|
225 relpipe-in-fstab | relpipe-tr-validator | sha512sum |
|
226 relpipe-in-fstab | sha512sum]]></m:pre> |
|
227 |
|
228 <p> |
|
229 This tool can be used for testing whether a file contains valid relational data: |
|
230 </p> |
|
231 |
|
232 <m:pre jazyk="bash"><![CDATA[ |
|
233 if relpipe-tr-validator < "some-file.rp" &> /dev/null; then |
|
234 echo "valid relational data"; |
|
235 else |
|
236 echo "garbage"; |
|
237 fi]]></m:pre> |
|
238 |
|
239 <p>or as a one-liner:</p> |
|
240 |
|
241 <m:pre jazyk="bash"><![CDATA[relpipe-tr-validator < "some-file.rp" &> /dev/null && echo "ok" || echo "error"]]></m:pre> |
|
242 |
|
243 <p> |
|
244 If an error is found, it is reported on STDERR. So just omit the <code>&</code> in order to see the error message. |
|
245 </p> |
|
246 |
|
247 |
|
248 <h2>/etc/fstab formatting using -in-fstab, -out-nullbyte, xargs and Perl</h2> |
|
249 |
|
250 <p> |
|
251 As we have seen before, we can convert <code>/etc/fstab</code> (or <code>mtab</code>) |
|
252 to e.g. an XML or a nice and colorful table using <m:name/>. |
|
253 But we can also convert these data back to the <code>fstab</code> format. And do it with proper indentation/padding. |
|
254 Fstab has a simple format where values are separated by one or more whitespace characters. |
|
255 But without proper indentation, these files look a bit obfuscated and hard to read (however, they are valid). |
|
256 </p> |
|
257 |
|
258 <m:pre jazyk="text" src="examples/relpipe-out-fstab.txt"/> |
|
259 |
|
260 <p> |
|
261 So let's build a pipeline that reformats the <code>fstab</code> and makes it more readable. |
|
262 </p> |
|
263 |
|
264 <m:pre jazyk="bash">relpipe-in-fstab | relpipe-out-fstab > reformatted-fstab.txt</m:pre> |
|
265 |
|
266 <p> |
|
267 We can hack together a script called <code>relpipe-out-fstab</code> that accepts relational data and produces <code>fstab</code> data. |
|
268 Later this will be probably implemented as a regular tool, but for now, it is just an example of a ad-hoc shell script: |
|
269 </p> |
|
270 |
|
271 <m:pre jazyk="bash" src="examples/relpipe-out-fstab.sh" odkaz="ano"/> |
|
272 |
|
273 <p> |
|
274 In the first part, we prepend a single record (<code>relpipe-in-cli</code>) before the data coming from STDIN (<code>cat</code>). |
|
275 Then, we use <code>relpipe-out-nullbyte</code> to convert relational data to values separated by a null-byte. |
|
276 This command processes only attribute values (skips relation and attribute names). |
|
277 Then we used <code>xargs</code> to read the null-separated values and execute a Perl command for each record (pass to it a same number of arguments, as we have attributes: <code>--max-args=7</code>). |
|
278 Perl does the actual formatting: adds padding and does some little tunning (merges two attributes and replaces empty values with <em>none</em>). |
|
279 </p> |
|
280 |
|
281 <p>This is formatted version of the <code>fstab</code> above:</p> |
|
282 |
|
283 <m:pre jazyk="text" src="examples/relpipe-out-fstab.formatted.txt"/> |
|
284 |
|
285 <p> |
|
286 And using following command we can verify, that the files differ only in comments and whitespace: |
|
287 </p> |
|
288 |
|
289 <pre>relpipe-in-fstab | relpipe-out-fstab | diff -w /etc/fstab -</pre> |
|
290 |
|
291 <p> |
|
292 Another check (should print same hashes): |
|
293 </p> |
|
294 |
|
295 <pre><![CDATA[relpipe-in-fstab | sha512sum |
|
296 relpipe-in-fstab | relpipe-out-fstab | relpipe-in-fstab | sha512sum]]></pre> |
|
297 |
|
298 <p> |
|
299 Regular implementation of <code>relpipe-out-fstab</code> will probably keep the comments |
|
300 (it needs also one more attribute and small change in <code>relpipe-in-fstab</code>). |
|
301 </p> |
|
302 |
|
303 <p> |
|
304 For just mere <code>fstab</code> reformatting, this approach is a bit overengineering. |
|
305 We could skip the whole relational thing and do just something like this: |
|
306 </p> |
|
307 |
|
308 <m:pre jazyk="bash">cat /etc/fstab | grep -v '^#' | sed -E 's/\s+/\n/g' | tr \\n \\0 | xargs -0 -n7 ...</m:pre> |
|
309 |
|
310 <p> |
|
311 plus prepend the comment (or do everything in Perl). |
|
312 But this example is intended as a demostration, how we can |
|
313 1) prepend some additional data before the data from STDIN |
|
314 2) use <m:name/> and traditional tools like <code>xargs</code> or <code>perl</code> together. |
|
315 And BTW we have implemented a (simple but working) <em>relpipe output filter</em> – and did it without any serious programming, just put some existing commands together :-) |
|
316 </p> |
|
317 |
|
318 <blockquote> |
|
319 <p> |
|
320 There is more Unix-nature in one line of shell script than there is in ten thousand lines of C. |
|
321 <m:podČarou>see <a href="http://www.catb.org/~esr/writings/unix-koans/ten-thousand.html">Master Foo and the Ten Thousand Lines</a></m:podČarou> |
|
322 </p> |
|
323 </blockquote> |
|
324 |
|
325 <h2>Writing an output filter in Bash</h2> |
|
326 |
|
327 <p> |
|
328 In previous example we created an output filter in Perl. |
|
329 We converted a relation to values separated by <code>\0</code> and then passed it through <code>xargs</code> to a perl <em>one-liner</em> (or a <em>multi-liner</em> in this case). |
|
330 But we can write such output filter in pure Bash without <code>xargs</code> and <code>perl</code>. |
|
331 Of course, it is still limited to a single relation (or it can process multiple relations of same type and do something like implicit <code>UNION ALL</code>). |
|
332 </p> |
|
333 |
|
334 <p> |
|
335 We will define a function that will help us with reading the <code>\0</code>-separated values and putting them into shell variables: |
|
336 </p> |
|
337 |
|
338 <m:pre jazyk="bash"><![CDATA[read_nullbyte() { for v in "$@"; do export "$v"; read -r -d '' "$v"; done }]]></m:pre> |
|
339 |
|
340 <!-- |
|
341 This version will not require the last \0: |
|
342 read_zero() { for v in "$@"; do export "$v"; read -r -d '' "$v" || [ ! -z "${!v}" ]; done } |
|
343 at least in case when the last value is not missing. |
|
344 Other values might be null/missing: \0\0 is OK. |
|
345 --> |
|
346 |
|
347 <p> |
|
348 Currently, there is no known way how to do this without a custom function (just with <code>read</code> built-in command of Bash and its parameters). |
|
349 But it is just a single line function, so not a big deal. |
|
350 </p> |
|
351 |
|
352 <p> |
|
353 And then we just read the values, put them in shell variables and process them in a cycle in a shell block of code: |
|
354 </p> |
|
355 |
|
356 <m:pre jazyk="bash"><![CDATA[relpipe-in-fstab \ |
|
357 | relpipe-out-nullbyte \ |
|
358 | while read_nullbyte scheme device mount_point fs_type options dump pass; do |
|
359 echo "Device ${scheme:+$scheme=}$device is mounted" \ |
|
360 "at $mount_point and contains $fs_type."; |
|
361 done]]></m:pre> |
|
362 |
|
363 <p> |
|
364 Which will print: |
|
365 </p> |
|
366 |
|
367 <pre><![CDATA[Device UUID=29758270-fd25-4a6c-a7bb-9a18302816af is mounted at / and contains ext4. |
|
368 Device /dev/sr0 is mounted at /media/cdrom0 and contains udf,iso9660. |
|
369 Device /dev/sde is mounted at /mnt/data and contains ext4. |
|
370 Device UUID=a2b5f230-a795-4f6f-a39b-9b57686c86d5 is mounted at /home and contains btrfs. |
|
371 Device /dev/mapper/sdf_crypt is mounted at /mnt/private and contains xfs.]]></pre> |
|
372 |
|
373 <p> |
|
374 Using this method, we can convert any single relation to any format (preferably some text one, but <code>printf</code> can produce also binary data). |
|
375 This is good for ad-hoc conversions and single-relation data. |
|
376 More powerful tools can be written in C++ and other languages like Java, Python, Guile etc. (when particular libraries are available). |
|
377 </p> |
|
378 |
|
379 <h2>Rename VG in /etc/fstab using relpipe-tr-sed</h2> |
|
380 |
|
381 <p> |
|
382 Assume that we have an <code>/etc/fstab</code> with many lines defining the mount-points (directories) of particular devices (disks) and we are using LVM. |
|
383 If we rename a volume group (VG), we have to change all of them. The lines look like this one: |
|
384 </p> |
|
385 |
|
386 <pre>/dev/alpha/photos /mnt/photos/ btrfs noauto,noatime,nodiratime 0 0</pre> |
|
387 |
|
388 <p> |
|
389 We want to change all lines from <code>alpha</code> to <code>beta</code> (the new VG name). |
|
390 This can be done by the power of regular expressions<m:podČarou>see <a href="https://en.wikibooks.org/wiki/Regular_Expressions/Simple_Regular_Expressions">Regular Expressions</a> at Wikibooks</m:podČarou> and this pipeline: |
|
391 </p> |
|
392 |
|
393 <m:pre jazyk="bash"><![CDATA[relpipe-in-fstab \ |
|
394 | relpipe-tr-sed 'fstab' 'device' '^/dev/alpha/' '/dev/beta/' \ |
|
395 | relpipe-out-fstab]]></m:pre> |
|
396 |
|
397 <p> |
|
398 The <code>relpipe-tr-sed</code> tool works only with given relation (<code>fstab</code>) and given attribute (<code>device</code>) |
|
399 and it would leave untouched other relations and attributes in the stream. |
|
400 So it would not replace the strings on unwanted places (if there are any random matches). |
|
401 </p> |
|
402 |
|
403 <p> |
|
404 Even the relation names and attribute names are specified as a regular expression, so we can (purposefully) modify multiple relations or attributes. |
|
405 For example we can put zeroes in both <code>dump</code> and <code>pass</code> attributes: |
|
406 </p> |
|
407 |
|
408 <m:pre jazyk="bash"><![CDATA[relpipe-in-fstab | relpipe-tr-sed 'fstab' 'dump|pass' '.+' '0' | relpipe-out-fstab]]></m:pre> |
|
409 |
|
410 <p> |
|
411 n.b. the data types must be respected, we can not e.g. put <code>abc</code> in the <code>pass</code> attribute because it is declared as <code>integer</code>. |
|
412 </p> |
|
413 |
|
414 <h2>Using relpipe-tr-sed with groups and backreferences</h2> |
|
415 |
|
416 <p> |
|
417 This tool also support regex groups and backreferences. Thus we can use parts of the matched string in our replacement string: |
|
418 </p> |
|
419 |
|
420 <m:pre jazyk="bash"><![CDATA[relpipe-in-cli generate r 1 a string "some string xxx_123 some zzz_456 other" \ |
|
421 | relpipe-tr-sed 'r' 'a' '([a-z]{3})_([0-9]+)' '$2:$1' \ |
|
422 | relpipe-out-tabular]]></m:pre> |
|
423 |
|
424 <p>Which would convert this:</p> |
|
425 <pre><![CDATA[r: |
|
426 ╭────────────────────────────────────────╮ |
|
427 │ a (string) │ |
|
428 ├────────────────────────────────────────┤ |
|
429 │ some string xxx_123 some zzz_456 other │ |
|
430 ╰────────────────────────────────────────╯ |
|
431 Record count: 1]]></pre> |
|
432 |
|
433 <p>into this:</p> |
|
434 <pre><![CDATA[r: |
|
435 ╭────────────────────────────────────────╮ |
|
436 │ a (string) │ |
|
437 ├────────────────────────────────────────┤ |
|
438 │ some string 123:xxx some 456:zzz other │ |
|
439 ╰────────────────────────────────────────╯ |
|
440 Record count: 1]]></pre> |
|
441 |
|
442 <p> |
|
443 If there were any other relations or attributes in the stream, they would be unaffected by this transformation, |
|
444 becase we specified <code>'r' 'a'</code> instead of some wider regular expression that would match more relations or attributes. |
|
445 </p> |
|
446 |
|
447 <h2>Filter /etc/fstab using relpipe-tr-grep</h2> |
|
448 |
|
449 <p> |
|
450 If we are interested only in certain records in some relation, we can filter it using <code>relpipe-tr-grep</code>. |
|
451 If we want to list e.g. only Btrfs and XFS file systems from our <code>fstab</code> (see above), we will run: |
|
452 </p> |
|
453 |
|
454 |
|
455 <m:pre jazyk="bash"><![CDATA[relpipe-in-fstab | relpipe-tr-grep 'fstab' 'type' 'btrfs|xfs' | relpipe-out-tabular]]></m:pre> |
|
456 |
|
457 <p>and we will get following filtered result:</p> |
|
458 <pre><![CDATA[fstab: |
|
459 ╭─────────────────┬──────────────────────────────────────┬──────────────────────┬───────────────┬──────────────────┬────────────────┬────────────────╮ |
|
460 │ scheme (string) │ device (string) │ mount_point (string) │ type (string) │ options (string) │ dump (integer) │ pass (integer) │ |
|
461 ├─────────────────┼──────────────────────────────────────┼──────────────────────┼───────────────┼──────────────────┼────────────────┼────────────────┤ |
|
462 │ UUID │ a2b5f230-a795-4f6f-a39b-9b57686c86d5 │ /home │ btrfs │ relatime │ 0 │ 2 │ |
|
463 │ │ /dev/mapper/sdf_crypt │ /mnt/private │ xfs │ relatime │ 0 │ 2 │ |
|
464 ╰─────────────────┴──────────────────────────────────────┴──────────────────────┴───────────────┴──────────────────┴────────────────┴────────────────╯ |
|
465 Record count: 2]]></pre> |
|
466 |
|
467 <p> |
|
468 Command arguments are similar to <code>relpipe-tr-sed</code>. |
|
469 Everything is a regular expression. |
|
470 Only relations matching the regex will be filtered, others will flow through the pipeline unmodified. |
|
471 If the attribute regex matches more attribute names, filtering will be done with logical OR |
|
472 i.e. the record is included if at least one of that attributes matches the search regex. |
|
473 </p> |
|
474 |
|
475 <p> |
|
476 If we need exact match of the whole attribute, we have to use something like <code>'^btrfs|xfs$'</code>, |
|
477 otherwise mere substring-match is enough to include the record. |
|
478 </p> |
|
479 |
|
480 <h2>SELECT mount_point FROM fstab WHERE type IN ('btrfs', 'xfs')</h2> |
|
481 |
|
482 <p> |
|
483 While reading classic pipelines involving <code>grep</code> and <code>cut</code> commands |
|
484 we must notice that there is some similarity with simple SQL queries looking like: |
|
485 </p> |
|
486 |
|
487 <m:pre jazyk="SQL">SELECT "some", "cut", "fields" FROM stdin WHERE grep_matches(whole_line);</m:pre> |
|
488 |
|
489 <p> |
|
490 And that is true: <code>grep</code> does restriction<m:podČarou> |
|
491 <a href="https://en.wikipedia.org/wiki/Selection_(relational_algebra)">selecting</a> only certain records from the original relation according to their match with given conditions</m:podČarou> |
|
492 and <code>cut</code> does projection<m:podČarou>limited subset of what <a href="https://en.wikipedia.org/wiki/Projection_(relational_algebra)">projection</a> means</m:podČarou>. |
|
493 Now we can do these relational operations using our relational tools called <code>relpipe-tr-grep</code> and <code>relpipe-tr-cut</code>. |
|
494 </p> |
|
495 |
|
496 <p> |
|
497 Assume that we need only <code>mount_point</code> fields from our <code>fstab</code> where <code>type</code> is <code>btrfs</code> or <code>xfs</code> |
|
498 and we want to do something (a shell script block) with these directory paths. |
|
499 </p> |
|
500 |
|
501 <m:pre jazyk="bash"><![CDATA[relpipe-in-fstab \ |
|
502 | relpipe-tr-grep 'fstab' 'type' '^btrfs|xfs$' \ |
|
503 | relpipe-tr-cut 'fstab' 'mount_point' \ |
|
504 | relpipe-out-nullbyte \ |
|
505 | while read -r -d '' m; do |
|
506 echo "$m"; |
|
507 done]]></m:pre> |
|
508 |
|
509 <p> |
|
510 The <code>relpipe-tr-cut</code> tool has similar syntax to its <em>grep</em> and <em>sed</em> siblings and also uses the power of regular expressions. |
|
511 In this case it modifies on-the-fly the <code>fstab</code> relation and drops all its attributes except the <code>mount_point</code> one. |
|
512 </p> |
|
513 |
|
514 <p> |
|
515 Then we pass the data to the Bash <code>while</code> cycle. |
|
516 In such simple scenario (just <code>echo</code>), we could use <code>xargs</code> as in examples above, |
|
517 but in this syntax, we can write whole block of shell commands for each record/value and do more complex actions with them. |
|
518 </p> |
|
519 |
|
520 <h2>More projections with relpipe-tr-cut</h2> |
|
521 |
|
522 <p> |
|
523 Assume that we have a simple relation containing numbers: |
|
524 </p> |
|
525 |
|
526 <m:pre jazyk="bash"><![CDATA[seq 0 8 \ |
|
527 | tr \\n \\0 \ |
|
528 | relpipe-in-cli generate-from-stdin numbers 3 a integer b integer c integer \ |
|
529 > numbers.rp]]></m:pre> |
|
530 |
|
531 <p>and second one containing letters:</p> |
|
532 |
|
533 <m:pre jazyk="bash"><![CDATA[relpipe-in-cli generate letters 2 a string b string A B C D > letters.rp]]></m:pre> |
|
534 |
|
535 <p>We saved them into two files and then combined them into a single file. We will work with them as they are a single stream of relations:</p> |
|
536 |
|
537 <m:pre jazyk="bash"><![CDATA[cat numbers.rp letters.rp > both.rp; |
|
538 cat both.rp | relpipe-out-tabular]]></m:pre> |
|
539 |
|
540 <p>Will print:</p> |
|
541 |
|
542 <pre><![CDATA[numbers: |
|
543 ╭─────────────┬─────────────┬─────────────╮ |
|
544 │ a (integer) │ b (integer) │ c (integer) │ |
|
545 ├─────────────┼─────────────┼─────────────┤ |
|
546 │ 0 │ 1 │ 2 │ |
|
547 │ 3 │ 4 │ 5 │ |
|
548 │ 6 │ 7 │ 8 │ |
|
549 ╰─────────────┴─────────────┴─────────────╯ |
|
550 Record count: 3 |
|
551 letters: |
|
552 ╭─────────────┬─────────────╮ |
|
553 │ a (string) │ b (string) │ |
|
554 ├─────────────┼─────────────┤ |
|
555 │ A │ B │ |
|
556 │ C │ D │ |
|
557 ╰─────────────┴─────────────╯ |
|
558 Record count: 2]]></pre> |
|
559 |
|
560 <p>We can put away the <code>a</code> attribute from the <code>numbers</code> relation:</p> |
|
561 |
|
562 <m:pre jazyk="bash">cat both.rp | relpipe-tr-cut 'numbers' 'b|c' | relpipe-out-tabular</m:pre> |
|
563 |
|
564 <p>and leave the <code>letters</code> relation unaffected:</p> |
|
565 |
|
566 <pre><![CDATA[numbers: |
|
567 ╭─────────────┬─────────────╮ |
|
568 │ b (integer) │ c (integer) │ |
|
569 ├─────────────┼─────────────┤ |
|
570 │ 1 │ 2 │ |
|
571 │ 4 │ 5 │ |
|
572 │ 7 │ 8 │ |
|
573 ╰─────────────┴─────────────╯ |
|
574 Record count: 3 |
|
575 letters: |
|
576 ╭─────────────┬─────────────╮ |
|
577 │ a (string) │ b (string) │ |
|
578 ├─────────────┼─────────────┤ |
|
579 │ A │ B │ |
|
580 │ C │ D │ |
|
581 ╰─────────────┴─────────────╯ |
|
582 Record count: 2]]></pre> |
|
583 |
|
584 <p>Or we can remove <code>a</code> from both relations resp. keep there only attributes whose names match <code>'b|c'</code> regex:</p> |
|
585 |
|
586 <m:pre jazyk="bash">cat both.rp | relpipe-tr-cut '.*' 'b|c' | relpipe-out-tabular</m:pre> |
|
587 |
|
588 <p>Instead of <code>'.*'</code> we could use <code>'numbers|letters'</code> and in this case it will give the same result:</p> |
|
589 |
|
590 <pre><![CDATA[numbers: |
|
591 ╭─────────────┬─────────────╮ |
|
592 │ b (integer) │ c (integer) │ |
|
593 ├─────────────┼─────────────┤ |
|
594 │ 1 │ 2 │ |
|
595 │ 4 │ 5 │ |
|
596 │ 7 │ 8 │ |
|
597 ╰─────────────┴─────────────╯ |
|
598 Record count: 3 |
|
599 letters: |
|
600 ╭─────────────╮ |
|
601 │ b (string) │ |
|
602 ├─────────────┤ |
|
603 │ B │ |
|
604 │ D │ |
|
605 ╰─────────────╯ |
|
606 Record count: 2]]></pre> |
|
607 |
|
608 <p>All the time, we are reducing the attributes. But we can also multiply them or change their order:</p> |
|
609 |
|
610 <m:pre jazyk="bash">cat both.rp | relpipe-tr-cut 'numbers' 'b|a|c' 'b' 'a' 'a' | relpipe-out-tabular</m:pre> |
|
611 |
|
612 <p> |
|
613 n.b. the order in <code>'b|a|c'</code> does not matter and if such regex matches, it preserves the original order of the attributes; |
|
614 but if we use multiple regexes to specify attributes, their order and count matters: |
|
615 </p> |
|
616 |
|
617 <pre><![CDATA[numbers: |
|
618 ╭─────────────┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────╮ |
|
619 │ a (integer) │ b (integer) │ c (integer) │ b (integer) │ a (integer) │ a (integer) │ |
|
620 ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ |
|
621 │ 0 │ 1 │ 2 │ 1 │ 0 │ 0 │ |
|
622 │ 3 │ 4 │ 5 │ 4 │ 3 │ 3 │ |
|
623 │ 6 │ 7 │ 8 │ 7 │ 6 │ 6 │ |
|
624 ╰─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────╯ |
|
625 Record count: 3 |
|
626 letters: |
|
627 ╭─────────────┬─────────────╮ |
|
628 │ a (string) │ b (string) │ |
|
629 ├─────────────┼─────────────┤ |
|
630 │ A │ B │ |
|
631 │ C │ D │ |
|
632 ╰─────────────┴─────────────╯ |
|
633 Record count: 2]]></pre> |
|
634 |
|
635 <p> |
|
636 The <code>letters</code> relation stays rock steady and <code>relpipe-tr-cut 'numbers'</code> does not affect it in any way. |
|
637 </p> |
|
638 |
|
639 |
|
640 <h2>Read an Atom feed using XQuery and relpipe-in-xml</h2> |
|
641 |
|
642 <p> |
|
643 Atom Syndication Format is a standard for publishing web feeds a.k.a web syndication. |
|
644 These feeds are usually consumed by a <em>feed reeder</em> that aggregates news from many websites and displays them in a uniform format. |
|
645 The Atom feed is an XML with a list of recent news containing their titles, URLs and short annotations. |
|
646 It also contains some metadata (website author, title etc.). |
|
647 </p> |
|
648 <p> |
|
649 Using this simple XQuery<m:podČarou>see <a href="https://en.wikibooks.org/wiki/XQuery">XQuery</a> at Wikibooks</m:podČarou> |
|
650 <em>FLWOR Expression</em> |
|
651 we convert the Atom feed into the XML serialization of relational data: |
|
652 </p> |
|
653 |
|
654 <m:pre jazyk="xq" src="examples/atom.xq" odkaz="ano"/> |
|
655 |
|
656 <p> |
|
657 This is similar operation to <a href="https://www.postgresql.org/docs/current/functions-xml.html">xmltable</a> used in SQL databases. |
|
658 It converts an XML tree structure to the relational form. |
|
659 In our case, the output is still XML, but in a format that can be read by <code>relpipe-in-xml</code>. |
|
660 All put together in a single shell script: |
|
661 </p> |
|
662 |
|
663 <m:pre jazyk="bash" src="examples/atom.sh"/> |
|
664 |
|
665 <p>Will generate a table with web news:</p> |
|
666 |
|
667 <m:pre jazyk="text" src="examples/atom.txt"/> |
|
668 |
|
669 <p> |
|
670 For frequent usage we can create a script or funcrion called <code>relpipe-in-atom</code> |
|
671 that reads Atom XML on STDIN and generates relational data on STDOUT. |
|
672 And then do any of these: |
|
673 </p> |
|
674 |
|
675 <m:pre jazyk="bash"><![CDATA[wget … | relpipe-in-atom | relpipe-out-tabular |
|
676 wget … | relpipe-in-atom | relpipe-out-csv |
|
677 wget … | relpipe-in-atom | relpipe-out-gui |
|
678 wget … | relpipe-in-atom | relpipe-out-nullbyte | while read_nullbyte published title url; do echo "$title"; done |
|
679 wget … | relpipe-in-atom | relpipe-out-csv | csv2rec | … |
|
680 ]]></m:pre> |
|
681 |
|
682 <p> |
|
683 There are several implementations of XQuery. |
|
684 <a href="http://galax.sourceforge.net/">Galax</a> is one of them. |
|
685 <a href="http://xqilla.sourceforge.net/">XQilla</a> or |
|
686 <a href="http://basex.org/basex/xquery/">BaseX</a> are another ones (and support newer versions of the standard). |
|
687 There are also XSLT processors like <a href="http://xmlsoft.org/XSLT/xsltproc2.html">xsltproc</a>. |
|
688 BaseX can be used instead of Galax – we just replace |
|
689 <code>galax-run -context-item /dev/stdin</code> with <code>basex -i /dev/stdin</code>. |
|
690 </p> |
|
691 |
|
692 <p> |
|
693 Reading Atom feeds in a terminal might not be the best way to get news from a website, |
|
694 but this simple example learns us how to convert arbitrary XML to relational data. |
|
695 And of course, we can generate multiple relations from a single XML using a single XQuery script. |
|
696 XQuery can be also used for operations like JOIN or UNION and for filtering and other transformations |
|
697 as will be shown in further examples. |
|
698 </p> |
|
699 |
|
700 <h2>Read files metadata using relpipe-in-filesystem</h2> |
|
701 |
|
702 <p> |
|
703 Our filesystems contain valuable information and using proper tools we can extract them. |
|
704 Using <code>relpipe-in-filesystem</code> we can gather metadata of our files and process them in relational way. |
|
705 This tools does not traverse our filesystem (remember the rule: <em>do one thing and do it well</em>), |
|
706 instead, it eats a list of file paths separated by <code>\0</code>. |
|
707 It is typically used together with the <code>find</code> command, but we can also create such list by hand using e.g. <code>printf</code> command or <code>tr \\n \\0</code>. |
|
708 </p> |
|
709 |
|
710 <m:pre jazyk="bash">find /etc/ssh/ -print0 | relpipe-in-filesystem | relpipe-out-tabular</m:pre> |
|
711 |
|
712 <p> |
|
713 In the basic scenario, it behaves like <code>ls -l</code>, just more modular and machine-readable: |
|
714 </p> |
|
715 |
|
716 <pre><![CDATA[filesystem: |
|
717 ╭───────────────────────────────────┬───────────────┬────────────────┬────────────────┬────────────────╮ |
|
718 │ path (string) │ type (string) │ size (integer) │ owner (string) │ group (string) │ |
|
719 ├───────────────────────────────────┼───────────────┼────────────────┼────────────────┼────────────────┤ |
|
720 │ /etc/ssh/ │ d │ 0 │ root │ root │ |
|
721 │ /etc/ssh/moduli │ f │ 553122 │ root │ root │ |
|
722 │ /etc/ssh/ssh_host_ecdsa_key │ f │ 227 │ root │ root │ |
|
723 │ /etc/ssh/sshd_config │ f │ 3262 │ root │ root │ |
|
724 │ /etc/ssh/ssh_host_ed25519_key.pub │ f │ 91 │ root │ root │ |
|
725 │ /etc/ssh/ssh_host_ecdsa_key.pub │ f │ 171 │ root │ root │ |
|
726 │ /etc/ssh/ssh_host_rsa_key │ f │ 1679 │ root │ root │ |
|
727 │ /etc/ssh/ssh_config │ f │ 1580 │ root │ root │ |
|
728 │ /etc/ssh/ssh_host_ed25519_key │ f │ 399 │ root │ root │ |
|
729 │ /etc/ssh/ssh_import_id │ f │ 338 │ root │ root │ |
|
730 │ /etc/ssh/ssh_host_rsa_key.pub │ f │ 391 │ root │ root │ |
|
731 ╰───────────────────────────────────┴───────────────┴────────────────┴────────────────┴────────────────╯ |
|
732 Record count: 11]]></pre> |
|
733 |
|
734 <p> |
|
735 We can specify desired attributes and also their aliases: |
|
736 </p> |
|
737 |
|
738 <m:pre jazyk="bash"><![CDATA[find /etc/ssh/ -print0 \ |
|
739 | relpipe-in-filesystem \ |
|
740 --file path --as artefact \ |
|
741 --file size \ |
|
742 --file owner --as dear_owner \ |
|
743 | relpipe-out-tabular]]></m:pre> |
|
744 |
|
745 <p>And we will get a subset with renamed attributes:</p> |
|
746 |
|
747 <pre><![CDATA[filesystem: |
|
748 ╭───────────────────────────────────┬────────────────┬─────────────────────╮ |
|
749 │ artefact (string) │ size (integer) │ dear_owner (string) │ |
|
750 ├───────────────────────────────────┼────────────────┼─────────────────────┤ |
|
751 │ /etc/ssh/ │ 0 │ root │ |
|
752 │ /etc/ssh/moduli │ 553122 │ root │ |
|
753 │ /etc/ssh/ssh_host_ecdsa_key │ 227 │ root │ |
|
754 │ /etc/ssh/sshd_config │ 3262 │ root │ |
|
755 │ /etc/ssh/ssh_host_ed25519_key.pub │ 91 │ root │ |
|
756 │ /etc/ssh/ssh_host_ecdsa_key.pub │ 171 │ root │ |
|
757 │ /etc/ssh/ssh_host_rsa_key │ 1679 │ root │ |
|
758 │ /etc/ssh/ssh_config │ 1580 │ root │ |
|
759 │ /etc/ssh/ssh_host_ed25519_key │ 399 │ root │ |
|
760 │ /etc/ssh/ssh_import_id │ 338 │ root │ |
|
761 │ /etc/ssh/ssh_host_rsa_key.pub │ 391 │ root │ |
|
762 ╰───────────────────────────────────┴────────────────┴─────────────────────╯ |
|
763 Record count: 11]]></pre> |
|
764 |
|
765 <p> |
|
766 We can also choose, which path format fits our needs best: |
|
767 </p> |
|
768 |
|
769 |
|
770 <m:pre jazyk="bash"><![CDATA[find ../../etc/ssh/ -print0 \ |
|
771 | relpipe-in-filesystem \ |
|
772 --file path \ |
|
773 --file path_absolute \ |
|
774 --file path_canonical \ |
|
775 --file name \ |
|
776 | relpipe-out-tabular]]></m:pre> |
|
777 |
|
778 <p>The <code>path</code> attribute contains the exact same value as was on input. Other formats are derived:</p> |
|
779 |
|
780 <pre><![CDATA[filesystem: |
|
781 ╭────────────────────────────────────────┬───────────────────────────────────────────────────┬───────────────────────────────────┬──────────────────────────╮ |
|
782 │ path (string) │ path_absolute (string) │ path_canonical (string) │ name (string) │ |
|
783 ├────────────────────────────────────────┼───────────────────────────────────────────────────┼───────────────────────────────────┼──────────────────────────┤ |
|
784 │ ../../etc/ssh/ │ /home/hack/../../etc/ssh/ │ /etc/ssh │ │ |
|
785 │ ../../etc/ssh/moduli │ /home/hack/../../etc/ssh/moduli │ /etc/ssh/moduli │ moduli │ |
|
786 │ ../../etc/ssh/ssh_host_ecdsa_key │ /home/hack/../../etc/ssh/ssh_host_ecdsa_key │ /etc/ssh/ssh_host_ecdsa_key │ ssh_host_ecdsa_key │ |
|
787 │ ../../etc/ssh/sshd_config │ /home/hack/../../etc/ssh/sshd_config │ /etc/ssh/sshd_config │ sshd_config │ |
|
788 │ ../../etc/ssh/ssh_host_ed25519_key.pub │ /home/hack/../../etc/ssh/ssh_host_ed25519_key.pub │ /etc/ssh/ssh_host_ed25519_key.pub │ ssh_host_ed25519_key.pub │ |
|
789 │ ../../etc/ssh/ssh_host_ecdsa_key.pub │ /home/hack/../../etc/ssh/ssh_host_ecdsa_key.pub │ /etc/ssh/ssh_host_ecdsa_key.pub │ ssh_host_ecdsa_key.pub │ |
|
790 │ ../../etc/ssh/ssh_host_rsa_key │ /home/hack/../../etc/ssh/ssh_host_rsa_key │ /etc/ssh/ssh_host_rsa_key │ ssh_host_rsa_key │ |
|
791 │ ../../etc/ssh/ssh_config │ /home/hack/../../etc/ssh/ssh_config │ /etc/ssh/ssh_config │ ssh_config │ |
|
792 │ ../../etc/ssh/ssh_host_ed25519_key │ /home/hack/../../etc/ssh/ssh_host_ed25519_key │ /etc/ssh/ssh_host_ed25519_key │ ssh_host_ed25519_key │ |
|
793 │ ../../etc/ssh/ssh_import_id │ /home/hack/../../etc/ssh/ssh_import_id │ /etc/ssh/ssh_import_id │ ssh_import_id │ |
|
794 │ ../../etc/ssh/ssh_host_rsa_key.pub │ /home/hack/../../etc/ssh/ssh_host_rsa_key.pub │ /etc/ssh/ssh_host_rsa_key.pub │ ssh_host_rsa_key.pub │ |
|
795 ╰────────────────────────────────────────┴───────────────────────────────────────────────────┴───────────────────────────────────┴──────────────────────────╯ |
|
796 Record count: 11]]></pre> |
|
797 |
|
798 <p> |
|
799 We can also <em>select</em> symlink targets or their types. |
|
800 If some file is missing or is inaccessible due to permissions, only <code>path</code> is printed for it. |
|
801 </p> |
|
802 |
|
803 <p> |
|
804 Tip: if we are looking for files in the current directory and want omit the „.“ we just call: <code>find -printf '%P\0'</code> instead of <code>find -print0</code>. |
|
805 </p> |
|
806 |
|
807 |
|
808 <h2>Using relpipe-in-filesystem to read extended attributes</h2> |
|
809 |
|
810 <p> |
|
811 Extended attributes (xattr) are additional <em>key=value</em> pairs that can be attached to our files. |
|
812 They are not stored inside the files, but on the filesystem. |
|
813 Thus they are independent of particular file format (which might not support metadata) |
|
814 and we can use them e.g. for tagging, cataloguing or adding some notes to our files. |
|
815 Some tools like GNU Wget use extended attributes to store metadata like the original URL from which the file was downloaded. |
|
816 </p> |
|
817 |
|
818 <m:pre jazyk="bash"><![CDATA[wget --recursive --level=1 https://relational-pipes.globalcode.info/ |
|
819 find -type f -printf '%P\0' \ |
|
820 | relpipe-in-filesystem --file path --file size --xattr xdg.origin.url \ |
|
821 | relpipe-out-tabular |
|
822 ]]></m:pre> |
|
823 |
|
824 <p>And now we know, where the files on our disk came from:</p> |
|
825 |
|
826 <pre><![CDATA[filesystem: |
|
827 ╭───────────────────────────┬────────────────┬────────────────────────────────────────────────────────────────────╮ |
|
828 │ path (string) │ size (integer) │ xdg.origin.url (string) │ |
|
829 ├───────────────────────────┼────────────────┼────────────────────────────────────────────────────────────────────┤ |
|
830 │ index.html │ 12159 │ https://relational-pipes.globalcode.info/v_0/ │ |
|
831 │ v_0/atom.xml │ 4613 │ https://relational-pipes.globalcode.info/v_0/atom.xml │ |
|
832 │ v_0/rss.xml │ 4926 │ https://relational-pipes.globalcode.info/v_0/rss.xml │ |
|
833 │ v_0/js/skript.js │ 2126 │ https://relational-pipes.globalcode.info/v_0/js/skript.js │ |
|
834 │ v_0/css/styl.css │ 2988 │ https://relational-pipes.globalcode.info/v_0/css/styl.css │ |
|
835 │ v_0/css/relpipe.css │ 1095 │ https://relational-pipes.globalcode.info/v_0/css/relpipe.css │ |
|
836 │ v_0/css/syntaxe.css │ 3584 │ https://relational-pipes.globalcode.info/v_0/css/syntaxe.css │ |
|
837 │ v_0/index.xhtml │ 12159 │ https://relational-pipes.globalcode.info/v_0/index.xhtml │ |
|
838 │ v_0/grafika/logo.png │ 3298 │ https://relational-pipes.globalcode.info/v_0/grafika/logo.png │ |
|
839 │ v_0/principles.xhtml │ 17171 │ https://relational-pipes.globalcode.info/v_0/principles.xhtml │ |
|
840 │ v_0/roadmap.xhtml │ 11097 │ https://relational-pipes.globalcode.info/v_0/roadmap.xhtml │ |
|
841 │ v_0/faq.xhtml │ 11080 │ https://relational-pipes.globalcode.info/v_0/faq.xhtml │ |
|
842 │ v_0/specification.xhtml │ 12983 │ https://relational-pipes.globalcode.info/v_0/specification.xhtml │ |
|
843 │ v_0/implementation.xhtml │ 10810 │ https://relational-pipes.globalcode.info/v_0/implementation.xhtml │ |
|
844 │ v_0/examples.xhtml │ 76958 │ https://relational-pipes.globalcode.info/v_0/examples.xhtml │ |
|
845 │ v_0/license.xhtml │ 65580 │ https://relational-pipes.globalcode.info/v_0/license.xhtml │ |
|
846 │ v_0/screenshots.xhtml │ 5708 │ https://relational-pipes.globalcode.info/v_0/screenshots.xhtml │ |
|
847 │ v_0/download.xhtml │ 5204 │ https://relational-pipes.globalcode.info/v_0/download.xhtml │ |
|
848 │ v_0/contact.xhtml │ 4940 │ https://relational-pipes.globalcode.info/v_0/contact.xhtml │ |
|
849 │ v_0/classic-example.xhtml │ 9539 │ https://relational-pipes.globalcode.info/v_0/classic-example.xhtml │ |
|
850 ╰───────────────────────────┴────────────────┴────────────────────────────────────────────────────────────────────╯ |
|
851 Record count: 20]]></pre> |
|
852 |
|
853 <p> |
|
854 If we like the BeOS/Haiku style, we can create empty files with some attributes attached and use our filesystem as a simple database |
|
855 and query it using relational tools. |
|
856 It will lack indexing, but for basic scenarios like <em>address book</em> it will be fast enough |
|
857 and we can feel a bit of BeOS/Haiku atmosphere in our contemporary GNU/Linux systems. |
|
858 But be careful with that because some editors delete and recreate files while saving them, which destroys the xattrs. |
|
859 Tools like <code>rsync</code> or <code>tar</code> with <code>--xattrs</code> option will backup our attributes securely. |
|
860 </p> |
|
861 |
|
862 |
29 |
863 </text> |
30 </text> |
864 |
31 |
865 </stránka> |
32 </stránka> |