A gawk program to mount all ISO images in a directory under a specified mount-point
Not a question, but I thought the use of the gawk "pipe" extension to process system commands might be interesting to others.
The problem: I'd downloaded several ISO images of the Baen Books CDs that they sometimes make available with their hard-cover books. (The CDs contain unencrypted copyrighted SF and fantasy books - several hundred of them - and non-commercial copying and distribution is permitted. Google "Baen CD" for sites distributing the images.) I tried to write this in bash, but I wanted the directory names to be the same as the CD names, and those names contain blanks and single quotes and other "special" characters, so the bash code was somewhat complicated. :) So, here's the code I wrote (with default source and destination locations which could, I hope, be overwritten with command-line arguments if the gawk code is invoked as a command. Here's the code: Code:
$ cat Scripts/gawk/mount_iso_images Code:
$ Scripts/gawk/mount_iso_images Code:
$ cat Scripts/bash/umount_iso_images |
Hi PTrenholm,
I think this is great! Not something I'd probably use any time soon, but I really like to examine the code in programs like this, just to see exactly what you did and how you did it, and to take ideas from it about doing bashy sorts of things, with not bash. This sort of falls into the category of "do it because you can" or "to see if you can", and I find is a fun way to get better with awk, sed, or whatever tool(s) it happens to be. Plus, (I find) some syntaxes & concepts, you simply can't wrap your head around by reading man pages- you actually got to use it to figure it out. All I can suggest (or ask actually) would be that you consider swapping out the PHP tags for regular code tags, as the PHP tags are terribly hard on the eyes, and they seem to cause weird sideways-scrolling issues with the webpage. :/ :) If you're interested in looking at some more awk stuff that are more than the usual one-liners, I wrote a few lately too. I wanted to use some pipes, some arrays, some system() commands, and some getline's, just to learn their usage & syntax. For example, this here just parses a log file, but I went crazy around how arguments/parameters were passed to the program and parsed, and on prettying up the program output. This other thing is an awk program which feeds Dzen2 statusbars on my screens (date/time, CPU, RAM, disks etc..). Rationale was: I thought using awk would reduce the CPU load/time consumed by it, since it runs in a loop all the time I'm logged into the desktop. It used to be written in bash. Anyhow, enough of my trying to steal your stage - thanks for posting your code! I'm sure someone will find it useful and/or educational. Best regards. |
Celine, thanks for the feedback. :) Per your suggestion, I changed the tags. (On my system, the php tags give a nice color-coded display which makes it somewhat easier to follow, but simple code tags work, and I didn't realize that they presented a problem for other systems. Sorry.)
As you probably know, as soon as you post some code, you see a problems and awkwardness that you like to fix. So, here's my "improved" 0.2 version of the code: Code:
#!/bin/gawk -f |
Of course :) every time I post some code to share, I end up updating it many times :D so the version numbers are always creeping up. As for the loopback limit, it is indeed a kernel compile option, however:
You may find this interesting, from /your/kernel/sources/Documentation/kernel_parameters.txt: Code:
max_loop= [LOOP] Maximum number of loopback devices that can |
Celine, adding max_loop=16 to the GRUB kernel=line bumped the number of loop devices from 8 to 16. Thanks for the hint. (I added that as a comment to your "reputation," but I don't know if you see those comments.)
|
Nice work with the code and I too found it interesting. A personal point would be perhaps to incorporate a function or two for things that you repeat many times, ie like the
testing and printing of error messages: Code:
if (system(cmd)) { Like I said it is just a personal one :) Good luck with the next revision :) (hard to stop at just one :) ) |
Thanks grail :) that was a good suggestion, so here's version 1.0. designed to be run at the end of the boot process from /etc/rc.d/rc.local. (That's where Fedora keep the rc.local script. Other distributions use different locations. For example, Ubuntu uses /etc/rc.local.)
Code:
#!/bin/gawk -f |
So I have suggestions and questions:
1. if (exe=="") exe="rc.mount_iso_images.gawk" - This will always be true so the if test here is not required. 2. in_root=cmd_result("groups | grep -w root") - Personally I would use 'id -u' as it is less ambiguous (ie what if something strange, groups = a.root) 3. cmd_result and run_cmd - again I see a lot of similarities where a single function could easily handle both 4. loc and mount_point - see number 1 5. run_cmd("ls -1 " loc " > " temp, "system(" list ") call failed.", 3) - as list equals "ls -1 " loc " > " temp, should this not be: Code:
run_cmd(list, "system(" list ") call failed.", 3) 7. if (loops_used >= max_loops) - do you really need to use global variables in this test? Or maybe the test could be somehow implemented in the while loop calling the function? 8. name=cmd_result("basename \"" location "\"", "Failed to find the basename of \"" location ".\"") - not sure you need to go out to the shell to perform this task. gensub maybe? 9. Would you explain the following as I didn't understand the need for the replacement?: Code:
# Is it already mounted? (Look at /etc/mtab to see why we're replacing blanks by "\040".) |
grail, again. thanks for the feedback! :)
Here's the bash function used to call the gawk program from rc.local. As you can see, the variables you refer to in points 1 & 4 are set in the call, so they are, in fact, not always null. (The test is used to set default values if the variables are not set in the command line.) Code:
############################################################################# Point 3: The difference is that cmd_result return the result, whilst run_cmd returns the error code. I'm more comfortable with that separation, but I can see your point. Point 5: Yes, it would be clearer that way. Point 6: Note the extra spaces before the other "parameters." Remember that gawk functions are called by value, and that anything listed in the argument list "shadows" variables with the same name that would otherwise be "visible" in the function. Since gawk has no "local" (nor "global") directive, placing "unpassed" variables in the argument list is the conventional way to declare local variables. The extra spaces, by convention, denote the start of the local variable list. Point 7: I think I need the test there since loops_used is only incremented in the function, but it could be done outside. Either works. P0int 8: Perhaps, but the shell function is fairly fast, and the run_cmd function already available. Point 9: Here's what the /etc/mtab looks like when the file name contains blanks. I believe that the \040 in that output is literal. Code:
/ISO/1635\040-\040The\040Eastern\040Front.iso /mnt/ISO/1635\040-\040The\040Eastern\040Front iso9660 ro,loop=/dev/loop0 0 0 |
Thanks for the explanation on Point 6 ... this makes sense now :)
How does the \040 in Point 9 benefit the script? (sorry if I am not getting this one :( ) Lastly, on calling the script may i also suggest: Code:
/etc/rc.d/rc.mount_iso_images.gawk -v exe=mount_iso -v loc=/ISO -v mount_point=/mnt/ISO/ |
Note the first line of the script (/bin/gawk -f). The -f tells it that everything following from /dev/stdin is part of the code, so the -v ... on the command line cause syntax errors. You can use the built-in ARGC array to read argument values, but I found it awkward to do so without some somewhat convoluted code, so I opted for using the bash invocation. After all, rc.local is a bash program.
As to the \040, note that a cat /etc/mtab displays a literal \040 for the blank, not the ASCII blank that an octal 40 represents. So, to get grep to match in mtab when the file name contains a blank, I needed to replace the blank by literal \040 strings. (There may be an easier way to check to see if a specific file has been mounted, but that seemed to work.) Note that the program starts by un-mounting all iso9660 mounts and deleting any sub-directories of mount_poit, so the check may be redundant. I had put it in during development, and felt it was safer to leave it in the "final" code. |
Quote:
To show you my test case, I used the following: Code:
#!/usr/bin/awk -f Code:
$./test.awk -v name=grail Quote:
But at least now I understand why necessary for your machine :) |
All times are GMT -5. The time now is 02:57 AM. |