Bash to ash: Help converting a script?
Ok, the awesome members here at linuxquestions helped (understatement) me modify a pick a random card bash script into this beauty:
Code:
#!/bin/bash Now I'd like to pay it forward and help others by sharing the script. Problem is, bash in not normally installed on the target devices. I was able to expand my routers limited flash memory via usb and install Bash to make the script work but that's not going to work out so well for others. However, the target device does have ash. (#!/bin/sh) Can somebody please convert, or help convert this? Or is it not possible? awk is installed by default, I can list any others if you ask... Thank You. |
First, you don't want to modify the script whenever something minor changes. Create /etc/config/vpn_servers.conf and set the important variables there, e.g.
Code:
# Settings for the OpenVPN rotation. Code:
#!/bin/sh The script could be rewritten to use awk instead of mktemp cat od bc . Alternatively, you could use just perl or python depending on which you have installed. It is also simple enough to be rewritten in e.g. plain C, if you need to minimize the dependencies. |
Thank You for all the effort...
Not Installed: od, bc, perl, or python. Installed by default: /usr/bin/logger sh, find, wc, sed, mktemp, cat, and awk |
Okay, here is the script rewritten to use awk. Untested.
Code:
#!/bin/sh |
Nominal Animal probably has the better solution, as he appears to understand exactly what you want to do. His suggestion of an external config file is good.
But as a scripting exercise, here's my previous script converted into posix-compatible syntax, with a minimal number of alterations. I used the info listed here: http://mywiki.wooledge.org/Bashism The biggest challenge is the lack of arrays, but by using the positional parameters instead, it actually requires few other changes: Code:
#!/bin/sh Code:
set -- *.ovpn If I had realized this before, we could've also done the same in my original script too: Code:
servers=( *.ovpn ) |
I don't know what to say... You guys are too kind! A simple Thank You doesn't quite sum up my feelings.
Both scripts work out of the box, I'm sure that takes some skill. You guys did it blind. Sorry for the late reply but I have been playing with both scripts to see "how" they work. In David the H's I did change it to set -- *.ovpn as suggested. It's a nice change because it uncomplicates the script. I don't understand what the rand() * 10000 does. Why the 10000? Also I noticed you used eval and I remember you telling me in the past to try not using it. And again, Nominal Animal version is very clean looking and looks very professional. Please forgive me but I do not understand the need for the sleep command variables. Also since you built the defaults into it, I didn't see the need to use an external conf? Would this also be acceptable?: Code:
#!/bin/sh |
awk's rand function outputs numbers as a six-decimal-place floating-point between zero and one (i.e. "0.566305"). Multiplying the result by 10000 shifts it so there are four-digits in front of the decimal, which is then truncated into an integer with the %d (digit) printf option. You could use any multiplier 100 or greater, really.
Yes, I like to stress that eval is something that should generally be avoided whenever possible, and in most cases there are better solutions available. But it's not always bad; you just need to know when and how to use it properly. The main concern is to make sure that it only evaluates lines with known values in it; otherwise you're at risk of executing malicious code. http://mywiki.wooledge.org/BashFAQ/048 In this case, there's no risk because the script ensures that the variable to be evaluated only contains a nice safe number. But there really isn't much choice anyway, as there's no other reasonable way to indirectly reference the needed parameter in a posix shell. I think NA's point with the separate config file is that the script itself should hold only the processing code while things that are user-specified should be set as inputs to the script in some fashion, and in principle I agree. It allows you to change the settings as necessary without messing with the script itself. "Defaults" are of course just fallbacks that the script will use if it can't import the configured input values for some reason (e.g. the file becomes corrupted). It's generally a good idea to code for such contingencies. But if you're really confident the settings will never change, then I say go ahead and simply hard-code them. |
Quote:
Quote:
(As an aside, I think two different example solutions are always better than just one. It gives the reader multiple viewpoints to the problem, and may eventually lead to a better solution than either one alone. I think it is a very good thing you showed a different approach, David the H. Thank you.) I know of several largeish Linux clusters that use a script to maintain firewall settings, with the settings contained in the script itself. Roughly once a year somebody manages to lock up a cluster by mis-editing the script. I did write a replacement script that uses separate config files, and cancels the changes unless the user expressly and interactively confirms the changes, but it was rejected: apparently the rare firewall lock-up is less of a disruption than changing something that 'works'. Which is kind of my point: Learn the way to do things efficiently in the long term. You never know which script ends up being used for the next decade or so. Separating configuration from the script is one of the ways. It lowers the probability of typos due to changes. For simple variables, sourcing a config file (like in my example above) works well, and you can use Code:
sh -c '. path/to/config.file >/dev/null 2>/dev/null' || echo "Error in config file!" If you have the settings within the script, the only way you can test it is by running it. If you were to expand your script to a full service, you could do the check before reload -- like Apache does. Then, when an administrator modifies the configuration and issues sudo service this-service reload , the configuration is checked first. If there is a problem in the configuration, the service is not taken offline; the administrator only gets a warning that there is a b0rk in the config, so no changes are done. (It still surprises me how most admins still use restart instead of reload -- and then do a headdesk when their buggy changes stop the service from working, and get an angry call from their boss and/or client. As you can see from my examples above, avoiding stuff like that does not require that much more effort.) There is a secondary reason why I wrote the example script the way I did. I suspect you will eventually need to change the script from being a standalone script running always, to either a script run regularly via cron, or to a full-blown service. The former is trivial; just omit the loop and the sleep $INTERVAL bit. For the latter, I'd personally write a small C program instead, mainly to keep resource use minimal. The logic would follow the script very closely, though. (The typical reason for moving to cron or a service script is ease of maintenance, for example through a web-based interface. Standalone scripts tend to be a pain to manage automatically -- each one needing their own management stuff --, while cron scripts and services follow very simple rules, and are therefore much easier to manage: you only need one interface to manage all possible cron scripts, and one interface to manage all service scripts, regardless of the script contents.) |
All times are GMT -5. The time now is 11:13 AM. |