Corning Community College CSCS1730 UNIX/Linux Fundamentals ======Project: PROCESSING WARRANTED NUMBERS (pwn0)====== =====Errata===== * any bugfixes or project updates will be posted here =====Toolbox===== In addition to the tools you're already familiar with, it is recommended you check out the following tools for possible application in this project (you may not need to use them in your solution, but they definitely offer up functionality that some solutions can make use of): * **bc(1)** * **comm(1)** * **diff(1)** * **grep(1)** * **join(1)** * **printf(1)** * **seq(1)** * **sort(1)** * **uniq(1)** =====Reference===== For additional information on the **printf(1)** tool recommended for use in this project, please check out this paper: * {{:haas:printf.pdf|printf PDF}} NOTE that while it focuses on the C and Perl variants of printf, the conceptual underpinnings are the same, and many of the examples are likely portable to the UNIX variant of the tool. =====Objective===== To create a script that can, with or without information provided by the user, display useful reference data in various number bases. =====Background===== We've encountered many things in our UNIX journey so far, and have been readily building upon past experiences as we achieve newer and better successes. Of particular focus, we've been looking at shell scripting, and that will be the primary focus of this week's project. There are collections of information quite useful to anyone journeying in a discipline, and computing is no different: We often encounter information represented in certain units more than others, or various quantities, or even particular number bases. ====binary/octal/hex==== One particular collection of information involves that of binary, octal, decimal (in signed and unsigned capacities) and hexadecimal numbers. We may encounter a tool that gives us information in octal and we need to feed it into another tool as binary or decimal; these sorts of transactions are common and crop up time and time again. Having a strong handle on this information is vital for on-going success in the computing field. As such, it is important to have close at hand the knowledge of the following number bases: * base 2 (binary) * base 8 (octal) * base 10 (decimal, specifically signed and unsigned quantities) * base 16 (hexadecimal) While we don't need to have hundreds of values memorized, it IS a good idea to have a rather quick recollection of enough values that can be used for debugging purposes. The first 16 values of each of these is such a preferred range. Not only does it give us a range of information, but for the powers-of-two bases, can actually fit in and be propagated quite nicely with little added effort. =====Process===== It is your task to write a script that, when run without any arguments, produces the following table of information (and displayed/formatted in this exact manner): lab46:~/src/unix/pwn0$ ./pwn0.sh base 2 | base 8 | base10 | base16 | ----------+----------+----------+----------+ 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 10 | 2 | 2 | 2 | 11 | 3 | 3 | 3 | 100 | 4 | 4 | 4 | 101 | 5 | 5 | 5 | 110 | 6 | 6 | 6 | 111 | 7 | 7 | 7 | 1000 | 10 | 8 | 8 | 1001 | 11 | 9 | 9 | 1010 | 12 | 10 | A | 1011 | 13 | 11 | B | 1100 | 14 | 12 | C | 1101 | 15 | 13 | D | 1110 | 16 | 14 | E | 1111 | 17 | 15 | F | lab46:~/src/unix/pwn0$ Your script should also take the following optional arguments (in this order): * **$1**- starting value (some value greater than or equal to 0, and less than 256) * **$2**- ending value (some value greater than or equal to 0, and less than 256) Numerical arguments are to be given in decimal. Additional arguments your script should support: * **by#** - iterate through values by # (default: 1) * **with#** - include base # in the list of bases to display * **sans#** - remove base # in the list of bases to display * **help** - display script usage and exit With any of these arguments validly provided, they should adjust the script's processing and output accordingly. Some additional constraints/assumptions you can make: * the minimum lower limit your script should process for is **0**. If an input goes negative, cap it to 0. * the maximum upper limit you can implement for is **255** (base 10). If an input goes in excess of this, cap it to 255. ====Only specifying starting value==== In the event only the starting value is specified, assume a count of 16 (so display 16 values, starting at the starting value). For example: lab46:~/src/unix/pwn0$ ./pwn0.sh 12 base 2 | base 8 | base10 | base16 | ----------+----------+----------+----------+ 1100 | 14 | 12 | C | 1101 | 15 | 13 | D | 1110 | 16 | 14 | E | 1111 | 17 | 15 | F | 10000 | 20 | 16 | 10 | 10001 | 21 | 17 | 11 | 10010 | 22 | 18 | 12 | 10011 | 23 | 19 | 13 | 10100 | 24 | 20 | 14 | 10101 | 25 | 21 | 15 | 10110 | 26 | 22 | 16 | 10111 | 27 | 23 | 17 | 11000 | 30 | 24 | 18 | 11001 | 31 | 25 | 19 | 11010 | 32 | 26 | 1A | 11011 | 33 | 27 | 1B | lab46:~/src/unix/pwn0$ ====Specifying starting and ending value==== We can adjust the range displayed by specifying an ending value as well: lab46:~/src/unix/pwn0$ ./pwn0.sh 37 42 base 2 | base 8 | base10 | base16 | ----------+----------+----------+----------+ 100101 | 45 | 37 | 25 | 100110 | 46 | 38 | 26 | 100111 | 47 | 39 | 27 | 101000 | 50 | 40 | 28 | 101001 | 51 | 41 | 29 | 101010 | 52 | 42 | 2A | lab46:~/src/unix/pwn0$ With a specified starting/ending value pair, we can display as little as 1 value and (theoretically) as many as we want. ====Going backwards==== It shouldn't matter the relationship of starting and ending values. In fact, if the starting value is HIGHER than the ending value, the count should go backwards: lab46:~/src/unix/pwn0$ ./pwn0.sh 73 59 base 2 | base 8 | base10 | base16 | ----------+----------+----------+----------+ 1001001 | 111 | 73 | 49 | 1001000 | 110 | 72 | 48 | 1000111 | 107 | 71 | 47 | 1000110 | 106 | 70 | 46 | 1000101 | 105 | 69 | 45 | 1000100 | 104 | 68 | 44 | 1000011 | 103 | 67 | 43 | 1000010 | 102 | 66 | 42 | 1000001 | 101 | 65 | 41 | 1000000 | 100 | 64 | 40 | 111111 | 77 | 63 | 3F | 111110 | 76 | 62 | 3E | 111101 | 75 | 61 | 3D | 111100 | 74 | 60 | 3C | 111011 | 73 | 59 | 3B | lab46:~/src/unix/pwn0$ ====Skipping values==== With the 'by#' argument, we can cause numbers to iterate by more than 1. For example, if we wanted to go from 3 to 81 skipping 3 values each turn: lab46:~/src/unix/pwn0$ ./pwn0.sh 3 81 by3 base 2 | base 8 | base10 | base16 | ----------+----------+----------+----------+ 11 | 3 | 3 | 3 | 110 | 6 | 6 | 6 | 1001 | 11 | 9 | 9 | 1100 | 14 | 12 | C | 1111 | 17 | 15 | F | 10010 | 22 | 18 | 12 | 10101 | 25 | 21 | 15 | 11000 | 30 | 24 | 18 | 11011 | 33 | 27 | 1B | 11110 | 36 | 30 | 1E | 100001 | 41 | 33 | 21 | 100100 | 44 | 36 | 24 | 100111 | 47 | 39 | 27 | 101010 | 52 | 42 | 2A | 101101 | 55 | 45 | 2D | 110000 | 60 | 48 | 30 | 110011 | 63 | 51 | 33 | 110110 | 66 | 54 | 36 | 111001 | 71 | 57 | 39 | 111100 | 74 | 60 | 3C | 111111 | 77 | 63 | 3F | 1000010 | 102 | 66 | 42 | 1000101 | 105 | 69 | 45 | 1001000 | 110 | 72 | 48 | 1001011 | 113 | 75 | 4B | 1001110 | 116 | 78 | 4E | 1010001 | 121 | 81 | 51 | lab46:~/src/unix/pwn0$ This should be flexible whether going forward or backward in count: lab46:~/src/unix/pwn0$ ./pwn0.sh 32 2 by4 base 2 | base 8 | base10 | base16 | ----------+----------+----------+----------+ 100000 | 40 | 32 | 20 | 11100 | 34 | 28 | 1C | 11000 | 30 | 24 | 18 | 10100 | 24 | 20 | 14 | 10000 | 20 | 16 | 10 | 1100 | 14 | 12 | C | 1000 | 10 | 8 | 8 | 100 | 4 | 4 | 4 | lab46:~/src/unix/pwn0$ ====Exceeding bounds==== Although our script does not need to iterate values outside the 0-255 range, it does need to check and cap values that exceed these bounds. For example, if a starting or ending value falls beyond 255, cap it to 255: lab46:~/src/unix/pwn0$ ./pwn0.sh 250 base 2 | base 8 | base10 | base16 | ----------+----------+----------+----------+ 11111010 | 372 | 250 | FA | 11111011 | 373 | 251 | FB | 11111100 | 374 | 252 | FC | 11111101 | 375 | 253 | FD | 11111110 | 376 | 254 | FE | 11111111 | 377 | 255 | FF | lab46:~/src/unix/pwn0$ If we have a negative value of any sort: lab46:~/src/unix/pwn0$ ./pwn0.sh 4 -8 base 2 | base 8 | base10 | base16 | ----------+----------+----------+----------+ 100 | 4 | 4 | 4 | 11 | 3 | 3 | 3 | 10 | 2 | 2 | 2 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | lab46:~/src/unix/pwn0$ Both starting and ending values need to appropriately fall within the 0-255 bounds, however they may be situated. ====Additional bases==== Your script should also support the inclusion of additional bases (valid bases are any base between 2 and 16, inclusive). The default bases assumed included are 2, 8, 10, and 16, but additional ones may be specified with a **with#** argument. For instance, let's say we wanted to include base 7: lab46:~/src/unix/pwn0$ ./pwn0.sh with7 base 2 | base 7 | base 8 | base10 | base16 | ----------+----------+----------+----------+----------+ 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 10 | 2 | 2 | 2 | 2 | 11 | 3 | 3 | 3 | 3 | 100 | 4 | 4 | 4 | 4 | 101 | 5 | 5 | 5 | 5 | 110 | 6 | 6 | 6 | 6 | 111 | 10 | 7 | 7 | 7 | 1000 | 11 | 10 | 8 | 8 | 1001 | 12 | 11 | 9 | 9 | 1010 | 13 | 12 | 10 | A | 1011 | 14 | 13 | 11 | B | 1100 | 15 | 14 | 12 | C | 1101 | 16 | 15 | 13 | D | 1110 | 20 | 16 | 14 | E | 1111 | 21 | 17 | 15 | F | lab46:~/src/unix/pwn0$ And this should work even with the potential inclusion of starting, ending, or a by# option (forward or backward): lab46:~/src/unix/pwn0$ ./pwn0.sh 13 255 by13 with7 base 2 | base 7 | base 8 | base10 | base16 | ----------+----------+----------+----------+----------+ 1101 | 16 | 15 | 13 | D | 11010 | 35 | 32 | 26 | 1A | 100111 | 54 | 47 | 39 | 27 | 110100 | 103 | 64 | 52 | 34 | 1000001 | 122 | 101 | 65 | 41 | 1001110 | 141 | 116 | 78 | 4E | 1011011 | 160 | 133 | 91 | 5B | 1101000 | 206 | 150 | 104 | 68 | 1110101 | 225 | 165 | 117 | 75 | 10000010 | 244 | 202 | 130 | 82 | 10001111 | 263 | 217 | 143 | 8F | 10011100 | 312 | 234 | 156 | 9C | 10101001 | 331 | 251 | 169 | A9 | 10110110 | 350 | 266 | 182 | B6 | 11000011 | 366 | 303 | 195 | C3 | 11010000 | 415 | 320 | 208 | D0 | 11011101 | 434 | 335 | 221 | DD | 11101010 | 453 | 352 | 234 | EA | 11110111 | 502 | 367 | 247 | F7 | lab46:~/src/unix/pwn0$ Multiple additional bases can be included... you merely append additional **with#** arguments: lab46:~/src/unix/pwn0$ ./pwn0.sh 196 2 with3 by14 with12 base 2 | base 3 | base 8 | base10 | base12 | base16 | ----------+----------+----------+----------+----------+----------+ 11000100 | 21021 | 304 | 196 | 144 | C4 | 10110110 | 20202 | 266 | 182 | 132 | B6 | 10101000 | 20020 | 250 | 168 | 120 | A8 | 10011010 | 12201 | 232 | 154 | 10A | 9A | 10001100 | 12012 | 214 | 140 | B8 | 8C | 1111110 | 11200 | 176 | 126 | A6 | 7E | 1110000 | 11011 | 160 | 112 | 94 | 70 | 1100010 | 10122 | 142 | 98 | 82 | 62 | 1010100 | 10010 | 124 | 84 | 70 | 54 | 1000110 | 2121 | 106 | 70 | 5A | 46 | 111000 | 2002 | 70 | 56 | 48 | 38 | 101010 | 1120 | 52 | 42 | 36 | 2A | 11100 | 1001 | 34 | 28 | 24 | 1C | 1110 | 112 | 16 | 14 | 12 | E | lab46:~/src/unix/pwn0$ ====Omitting bases==== With the **sans#** option, we can leave out specified bases (useful for excluding any of the default 2, 8, 10, or 16). Here we will omit base 8 from a default run: lab46:~/src/unix/pwn0$ ./pwn0.sh sans8 base 2 | base10 | base16 | ----------+----------+----------+ 0 | 0 | 0 | 1 | 1 | 1 | 10 | 2 | 2 | 11 | 3 | 3 | 100 | 4 | 4 | 101 | 5 | 5 | 110 | 6 | 6 | 111 | 7 | 7 | 1000 | 8 | 8 | 1001 | 9 | 9 | 1010 | 10 | A | 1011 | 11 | B | 1100 | 12 | C | 1101 | 13 | D | 1110 | 14 | E | 1111 | 15 | F | lab46:~/src/unix/pwn0$ Of course, this option can be validly mixed with any of the other valid options, and needs to work as appropriate (here you'll see a run we did prior, with the only difference being the omission of base 8): lab46:~/src/unix/pwn0$ ./pwn0.sh 196 2 with3 by14 with12 sans8 base 2 | base 3 | base10 | base12 | base16 | ----------+----------+----------+----------+----------+ 11000100 | 21021 | 196 | 144 | C4 | 10110110 | 20202 | 182 | 132 | B6 | 10101000 | 20020 | 168 | 120 | A8 | 10011010 | 12201 | 154 | 10A | 9A | 10001100 | 12012 | 140 | B8 | 8C | 1111110 | 11200 | 126 | A6 | 7E | 1110000 | 11011 | 112 | 94 | 70 | 1100010 | 10122 | 98 | 82 | 62 | 1010100 | 10010 | 84 | 70 | 54 | 1000110 | 2121 | 70 | 5A | 46 | 111000 | 2002 | 56 | 48 | 38 | 101010 | 1120 | 42 | 36 | 2A | 11100 | 1001 | 28 | 24 | 1C | 1110 | 112 | 14 | 12 | E | lab46:~/src/unix/pwn0$ Multiple **sans#** options can be given, and will cancel out any present base (including those specified with a **with#** option). A **sans#** option for a non-existent base should be silently ignored. ====Online help==== Your script should also, when prompted, display usage information with the **help** option: lab46:~/src/unix/pwn0$ ./pwn0.sh help pwn0.sh - render a number table in the specified bases, displaying from starting value to ending value. behavior: by default, bases 2, 8, 10, and 16 will be displayed, in a range of 0 to 15 (inclusive). note: valid bases range from 2-16, and values should not go negative nor exceed 255. If a starting value but no ending value is given, assume an ending value +15 away (or capped by 255). usage: pwn0.sh [start#] [end#] [OPTION]... options: $1 - if a positive decimal value, override the starting value (0) $2 - if a positive decimal value, override the ending value (15) other options: by# - iterate through values by # (default: 1) with# - include base # in the list of bases to display sans# - remove base # in the list of bases to display help - display script usage and exit lab46:~/src/unix/pwn0$ =====Specifications===== Evaluation will be based on correctness of values as well as on formatting/spacing. You'll notice that everything lines up and is positioned similarly: * each column exists within 10 characters of on-screen real estate: * 8 characters for actual value display, * with one space padding either side * columns are separated with a '|' symbol, appended to the right edge of the column (effectively making each column 11 characters wide). * note that the rightmost column is ended with this '|'. * there is a heading/header at the top of the output table, and it uses a '**+**' to "connect" the horizontal and vertical lines being drawn * The dashes and vertical bars set up an ASCII art-like table pattern that helps make the data more readable. * The values are right justified with each column * justification is essential. Everything should line up on its respective right margin. * base headings are right justified, with a 2 spaces being allocated for reference of the base: * you'll see "base 2" vs. "base16"; your script needs to accommodate this. * You are to literally print out no more than 2 consecutive spaces at a time in your output statements. If you need more, use one of the formatting tools to generate spaces for you (**printf(1)** may be able to help you with this). * You need to check your arguments to ensure they are present and valid. Some examples: * starting value is a valid (whole) number * if less than 0, or greater than 255, needs to be capped to whichever extreme it exceeds * if -4, cap to 0 * if 300, cap to 255 * ending value is a valid (whole) number * if less than 0, or greater than 255, needs to be capped to whichever extreme it exceeds * if -8, cap to 0 * if 420, cap to 255 * any invalid arguments (nonsensical value in place of starting value, invalid base specification, etc.) should be silently dropped/ignored. * default starting value is 0, default ending value is 15. If nothing else is validly provided, these are the values the script should run with. * if starting and ending values are the same, the script will display just one line of number output (in addition to the header). * if lacking any bases to display, silently exit * your script needs to commence with a proper **shabang** to bash; your script needs to end with an "**exit 0**" at the very end * comments and indentation are required and necessary * comments should explain how or why you are doing something * indentation should be consistent throughout the script * indentation is to be no less than 3 on-screen spaces (I recommend tabstops of 4). * continuing with our shell scripting, your script will need to employ in a core/central way: * variables * command expansions * regular expressions * if statements * loops * at least 1 function (use **function** keyword to identify) * your logic needs to: * flow (one thing leads into the next, as best as possible) * make sense within the given context * avoid redundancy (make a function instead of maintaining multiple copies of code) * be understood by you (no grabbing snippets that seem to "work" from the internet) * if you gain inspiration from some external resource, please cite it * comments are a great way of demonstrating understanding (if you explain the why and how effectively, and it isn't in violation of other aspects, I'll know you are in control of things) To be sure, I'll be checking to make sure you solution follows the spirit of what this project is about (that you implement functional, flowing logic utilizing the tools and concepts we've learned, in an application that helps demonstrate your comprehension). Don't try to weasel your way out of this or cut corners. This is an opportunity to further solidify your proficiency with everything. =====Spirit of project===== The spirit of the project embodies many aspects we've been focusing on throughout the semester: * recognizing patterns to employ effective solutions in problem solving * utilizing concepts and tools covered * demonstrating comprehension of concepts, tools, and problems * employing concepts in knowledgeable and thoughtful manner * following instructions * implementing to specifications * utilizing creativity * being able to control solution via consistent, clear, and organized presentation Basically: I want your solution to be the result of an honest, genuine brainstorming process where you have (on your own) figured out a path to solving the problem, you have dabbled and experimented and figured things out, and you can command the concepts and tools with a fluency enabling you to pull off such a feat. Your solution should demonstrate the real learning that took place and experience gained. Cutting corners, avoiding work, skimping on functionality, cheating through getting others to do work for you or finding pre-packaged "answers" on the internet violates the spirit of the project, for they betray your ability to pull off the task on your own. =====Identifying shortcomings===== I would also like it if you provided an inventory of what functionality is lacking or out of spec when you submit the project. The better you can describe your deviations from stated requirements, the more forgiving I may be during evaluation (versus trying to hide the shortcomings and hoping I do not discover them). The more fluent you are in describing your shortcomings on accomplishing the project (ie "I didn't know how to do this" is far from fluent), the better I will be able to gauge your understanding on a particular aspect. This can be in the form of comments in your script, or even a separate file submitted at time of submission (if a file, be sure to make mention of it in your script so I can be sure to look for it). =====Output Formatting with printf(1)===== You might be wondering how you can full off some of these output formatting feats. The **echo** command, after all, is rather rudimentary. Fear not! The **printf(1)** tool comes to your rescue! Like **echo**, **printf** displays information to the screen (STDOUT). In fact, various programming languages (like C and C++) that want more powerful output formatting implement some form of printf into their vast libraries. Be sure to check the manual page for options and functionality; following will be a few usage examples. ===Basic operation=== Like **echo**, **printf** in its simplest form takes as an argument a string to display to STDOUT: lab46:~$ printf "Hello, World" Hello, Worldlab46:~$ However, we notice one difference between the default behavior of **printf** vs. **echo**: **printf** does not automatically issue a newline by default, as **echo** does. So, we'll need to specify it manually with the **\n** character: lab46:~$ printf "Hello, World\n" Hello, World lab46:~$ Those various escape characters we've learned about? Super useful here: * **\n** - newline * **\t** - tab Be sure to check the manual page for additional escape characters. ===Format specifiers=== Now, we know from using **echo** we can utilize shell variable and command expansions to make our output more dynamic. But, **printf** adds additional formatting capability that **echo** lacks. If the string we are displaying contains **%** symbols, substitutions can be made and behaviors invoked; functionality that **echo** lacks. Some common format specifiers: * **%d** - substitute integer (whole number) value * **%s** - substitute string value * **%c** - substitute single character * **%f** - substitute floating point value * **%o** - substitute integer (whole number) value and represent in octal * **%x** - substitute integer (whole number) value and represent in hexadecimal First up, a simple example: lab46:~$ printf "%d\n" 175 175 lab46:~$ Then, adding to it: lab46:~$ printf "The number: %d\n" 175 The number: 175 lab46:~$ That 175 can also be in a variable: lab46:~$ value=175 lab46:~$ printf "The number: %d\n" ${value} The number: 175 lab46:~$ You may want to wrap the variable in double quotes, to avoid cases where it might be NULL and otherwise generate an error (I generally quote all my variables to be on the safe side). How about substitutions with strings? lab46:~$ printf "Your username is: %s\n" "${USER}" Your username is: username lab46:~$ And in general just crafting super effective format strings: lab46:~$ value=175 lab46:~$ printf "%d %s %X\n" "${value}" "is hexadecimal" "${value}" 175 is hexadecimal AF lab46:~$ Please take note, where there are multiple format specifiers, substitution is in order of specification... **%d** was the first, **%s** was the second, and **%X** was the third in that particular string, so the first value following the string is grabbed by the **%d**, the next the **%s**, and the third the **%X**. Ordering matters (which should make sense). ===Output formatting=== So now, getting to where **printf** really excels, formatting your output. It turns out, that between the **%** and whatever format option you specify, you can provide a numeric value which will impact how that number appears on the screen, commonly in the form of preallocated space to display within. For instance: lab46:~$ printf "Testing: >>%4d<<\n" 47 Testing: >> 47<< lab46:~$ See what happened there? 47 was displayed, but WITHIN a block of 4 characters. Also of note, by default output is **right justified**. To **left** justify, simply make it negative: lab46:~$ printf "Testing: >>%-4d<<\n" 47 Testing: >>47 << lab46:~$ We can also pad with zeros, that is represented by decimal values: lab46:~$ printf "Testing: >>%.3d<<\n" 49 Testing: >>049<< lab46:~$ This can be combined with justify and space allocation: lab46:~$ printf "Testing: >>%-6.3d<<\n" 49 Testing: >>049 << lab46:~$ Other neat tricks? We can variable-ize the padding by using a *****: lab46:~$ spacing=4 lab46:~$ printf "Testing: >>%*d<<\n" "${spacing}" 49 Testing: >> 49<< lab46:~$ The **printf** tool is super-powerful and useful for output, so mastering its use adds an impressive capability to your repertoire. Play with **printf** and experiment... you'll find it can accomplish some impressive output feats that previously may have been more complicated. =====Verification===== To assist you in verifying output compliance, I have generated a set of sample script outputs and placed them in the public directory for this project. There you will find a file by the name of **outputlist**, which contains a list of filenames (output0 - outputF) and the **pwn0.sh** script command-line used to generate the output found in those files. Using these files, you can check the functionality of your script in a number of operating conditions, to verify that it is indeed working as it should be. While this isn't likely to cover the full spectrum of possibilities, it should offer up some assistance in helping you broaden your view of troubleshooting scenarios. =====Submission===== By successfully performing this project, you should have a fully functioning script by the name of **pwn0.sh**, which is all you need to submit for project completion (no steps file, as your "steps" file IS the script you wrote). To submit this project to me using the **submit** tool, run the following command at your lab46 prompt: $ submit unix pwn0 pwn0.sh Submitting unix project "pwn0": -> pwn0.sh(OK) SUCCESSFULLY SUBMITTED You should get some sort of confirmation indicating successful submission if all went according to plan. If not, check for typos and or locational mismatches. I'll be looking for the following: 78:pwn0:final tally of results (78/78) *:pwn0:pwn0.sh on help displays informative usage and exits [4/4] *:pwn0:pwn0.sh effectively utilizes variables in operations [4/4] *:pwn0:pwn0.sh effectively utilizes command expansions [4/4] *:pwn0:pwn0.sh effectively utilizes regular expressions [4/4] *:pwn0:pwn0.sh effectively utilizes selection statements [4/4] *:pwn0:pwn0.sh effectively utilizes looping structures [4/4] *:pwn0:pwn0.sh effectively utilizes functions in script [4/4] *:pwn0:pwn0.sh is a proper bash script with shabang and exit [4/4] *:pwn0:pwn0.sh parses command-line arguments as appropriate [4/4] *:pwn0:pwn0.sh accurately displays values in proper alignment [4/4] *:pwn0:pwn0.sh accurately displays values in requested bases [4/4] *:pwn0:pwn0.sh accurately displays values in specified range [4/4] *:pwn0:pwn0.sh range logic flexibly works forward and reverse [4/4] *:pwn0:pwn0.sh iterates as appropriate in given scenarios [4/4] *:pwn0:pwn0.sh includes additional bases as requested [4/4] *:pwn0:pwn0.sh omits specified bases as requested [4/4] *:pwn0:pwn0.sh properly manages input violations [4/4] *:pwn0:pwn0.sh operates according to specifications [5/5] *:pwn0:pwn0.sh logic is organized and easy to read [5/5] Additionally: * Solutions not abiding by spirit of project will be subject to a 25% overall deduction * Solutions not utilizing descriptive why and how comments will be subject to a 25% overall deduction * Solutions not utilizing indentation to promote scope and clarity will be subject to a 25% overall deduction