Hey again,
After correcting a few of the train-wreck sentences I wrote for yesterday's post (note to self: must not think while typing ;) I figured I'd flip 180 degrees and move on to the subject of modifying kernel tunable parameters on Linux (specifically tested on RedHat) using sysctl. For the Solaris enthusiast out there (who hasn't worked with Linux all that much) /etc/sysctl.conf on RedHat Linux can be thought of as a rough equivalent of /etc/system on Solaris (The last time we came close to trying to draw a comparison between these two files was back in May in a post regarding safe patching of a Veritas root disk). The sysctl command on Linux doesn't translate quite so well to some older versions of Solaris (some things could be done with ndd, but less of some and more of the other - a vague resemblance at best). Newer versions of Solaris that make use of the "project" based configuration are more directly relatable (projadd, projdel, projmod and projects are four commands that all modify, or report on, kernel tunables on-the-fly like sysctl can, although they make use of a completely separate configuration file - /etc/project - to store values), but I digress... (After yesterday's train wreck sentences we have a run-on... coincidence? :)
As noted, sysctl is a very versatile command and can be used either in its standalone form, or through the modification of the /etc/sysctl.conf file. First, we'll take a brief look at what the sysctl standalone command can do. It doesn't have too many options, so explaining them quickly upfront will make the rest seem like it makes more sense ;) You can run sysctl with the following flags (maybe more, depending on your distro):
-a to display all the tunable key values currently available
-A to display all the tunable key values currently available, as well as table values
-e to ignore errors (specifically pertaining to unrecognized characters)
-n to "not" print the key names when printing out values
-N to "only" print the key names and forgo printing their values
-p (sometimes -P) to import and apply settings from a specified file. This option will use /etc/sysctl.conf as the default if no file name argument is provided on the command line
-q for your standard quiet mode
-w to change kernel tunable (sysctl) settings - This will make the change in real time, as well as update the /etc/sysctl.conf file
and two more "special" arguments:
variablename <-- Use this on its own to read a key from sysctl matching your variablename
variablename=value <-- Use this to set a variablename (key) to a specific value. Note that this needs to be used with the -w flag (which changes sysctl settings)
Some basic examples of sysctl's use would include:
host # sysctl -a <-- This will produce a huge list of output. The basic format would be: NAME TYPE CHANGEABLE - with each column's name accurately depicting what it represents. For instance you could get an entry with a NAME of "kernel.hostid" and a TYPE of "u_int" (note that this is the datatype) and a notation on whether or not it's CHANGEABLE - in this instance "yes" The changeable field can also return "no" and "raise only" It seems logical to assume that it could return "lower only" as well, but I've yet to see it.
SPECIAL ERRATA NOTICE: If sysctl -a spews a lot of kernel warnings, check out Advisory RHBA-2008:0020-4 on RedHat's website for a patch to fix that issue.
host # sysctl -p /etc/mytestsysctl.conf <-- this will read in and enact all the kernel changes specified in your special /etc/mytestsysctl.conf file. It's a good idea to use a different filename when testing out new sysctl.conf settings, especially if you're making broad changes, since, if you completely screw the pooch and your machine reboots, it will come back up looking for the default /etc/sysctl.conf which will still be good to go)
host # sysctl -p <-- Use the -p option without any arguments if you've made adjustments to your sysctl.conf file and want to reload it, or just to be sure that it is actually being read (if you have serious doubts, you can run "strace -f /sbin/sysctl -p" to get more granular information. If you find that you do need to use strace to run down a problem with sysctl, hopefully our previous post on using strace to debug application issues will help get you off on the right foot and an expedited solution)
host # sysctl -w kernel.hostname="Error.Dumping.core" <-- this will set the hostname of your machine to something that might possibly be amusing. Please ensure that your superiors (or the folks you work for) have a sense of humor before pulling a stunt like this and walking away ;)
It's interesting, also, to note that, while sysctl will work just fine with an /etc/sysctl.conf file that includes nothing but comments (or is completely non-existent), your /proc filesystem "must" be of the type "procfs" in order for it to function correctly. This is picking a nit, really, since you'd have to go out of your way to build your RedHat Linux box to use (for instance) ext3 for the /proc filesystem, but a bit of information that's good to know (maybe... at some point in the future ;) /proc/sys is the base directory for sysctl. In fact, if you wanted to emulate "sysctl -a", you could just do an ls in that directory.
Tomorrow, or sometime later this week, we'll take a look at some of the kernel tunables you'll probably want to change, or may have to modify, most often with sysctl and, with as even a hand as possible, debate the pro's and con's of some of the more "impactful" values that you can mess with.
Cheers,
, Mike
Tuesday, July 29, 2008
Using Sysctl To Change Kernel Tunables On Linux
Monday, May 26, 2008
How To Fake Associative Arrays In Bash
Greetings,
As promised in our previous post on working with associative arrays in Linux and Unix, we're back to tackle the subject of associative arrays in bash. As was noted, we're using bash version 2.05b.0(1) and (to my knowledge) bash ( up to, and including, bash 3.2 ) does not directly support associative arrays yet. You can, of course, create one-dimensional (or simple index) arrays, but hashing key/value pairs is still not quite there.
Today we'll check out how to emulate that same functionality in bash that can be found in Perl and Awk. First we'll initialize our array, even though we don't necessarily have to:
host # typeset -a MySimpleHash
To begin with, we'll have to consider what bash already does for us and how we want that to change. For our first example, let's take a look at what happens if we just make assignments to a bash array with, first, a numeric and then an alpha value:
host # MySimpleHash["bob"]=15
host # echo ${MySimpleHash["bob"]}
15
host # MySimpleHash["joe"]="jeff"
host # echo ${MySimpleHash["joe"]}
jeff
This seems to be working out okay, but if we look at the values again, it seems that MySimpleHash["bob"] gets reassigned after we assign the alpha value "jeff"to the key "joe" :
host # echo ${MySimpleHash["bob"]}
jeff
This behaviour repeats itself no matter if we mix integers with strings. Bash can't handle this natively (but, it never claimed it could :)
host # MySimpleHash["bob"]="john"
host # echo ${MySimpleHash["bob"]}
john
host # MySimpleHash["joe"]="jeff"
host # echo ${MySimpleHash["joe"]}
jeff
host # echo ${MySimpleHash["bob"]}
jeff
This looks as though it's going to necesitate an "eval" nightmare much much worse than faking arrays in the Bourne Shell! Ouch! However, we might be able to get around it with a little bit of "laziness" if we just construct two parallel arrays. This, of course, would necessitate keeping the values in both arrays equal and consistent. That is, if "bob" is the third key in our associative array, and "joe" is "bob"'s value, both need to be at the exact same numeric index in each regular array. Otherwise translation becomes not-impossible, but probably a real headache ;)
To demonstrate we'll create a simple "3 key/value pair" associative array using the double-regular-array method, like so:
host # typeset -a MySimpleKeys
host # typeset -a MySimpleVals
host # MySimpleKeys[0]="abc";MySimpleKeys[1]="ghi";MySimpleKeys[2]="mno"
host # MySimpleVals[0]="def";MySimpleVals[1]="jkl";MySimpleVals[2]="pqr"
Now, we should be able to "fake" associative array behaviour by calling the common index from each array (In this fake associative array we have key/value pairs of abc/def, ghi/jkl and mno/pqr). Now that we have the key/value pairs set up, we need to set up the "associative array" so that we can request the values by the key names, rather than the numeric array index. We'll do this in a script later, so our command line doesn't get any uglier:host # key=$1
host #for (( x=0 ; x < ${#MySimpleKeys[@]}; x++ ))
>do
> if [ $key == "${MySimpleKeys[$x]}" ]
> then
> echo "${MySimpleVals[$x]}"
> fi
>done
Testing this on the command line produces suitable, but pretty lame looking results:
host # ./MySimpleHash ghi
jkl
We're going to need to modify the script so that it takes its keys and just returns a value that doesn't need to be cropped (using "echo -n" will solve this nicely):
echo "${MySimpleVals[$x]}"
changes to
echo -n "${MySimpleVals[$x]}" <--- This is helpful if we want to use the result as a return value :)
Still, the shell is going to balk at whatever we do to try and pass an argument to the script like this:
host # ./MySimpleHash{ghi}
-bash: ./MySimpleHash{ghi}: No such file or directory
So, for now, we'll just make it so it's "almost" nice looking:
host # a=`./MySimpleHash {abc}`
host # echo $a
def
It'll get the job done, and could be used in a pinch, but is way too klunky to replace Awk or Perl's built-in associative array handling. Nevertheless, at least we have a way we can get around it if we "have" to :)
I've included the script written during this post below, but, if you want to check out a more complex (and, subsequently, much more elegant) script to fake associative arrays in Perl, you should take a look at this associative array hack for bash and also this bash hack that comes at the issue from a whole different angle.
I think you'll like either one of these scripts a lot better than this one, but hopefully we've both learned, at least a little bit, about the way associative arrays work (and how they differ from one dimensional arrays) in the process :)
Cheers,
This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License#!/bin/bash
# TerribleAAbashHack.sh
# What was I thinking?
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
typeset -a MySimpleKeys
typeset -a MySimpleVals
MySimpleKeys[0]="abc";MySimpleKeys[1]="ghi";MySimpleKeys[2]="mno"
MySimpleVals[0]="def";MySimpleVals[1]="jkl";MySimpleVals[2]="pqr"
key=`echo $@|sed -e 's/{//g' -e 's/}//g'`
for (( x=0 ; x < ${#MySimpleKeys[@]}; x++ ))
do
if [ $key == "${MySimpleKeys[$x]}" ]
then
echo -n "${MySimpleVals[$x]}"
fi
done
, Mike
Tuesday, April 22, 2008
Functions Vs. Subroutines In Perl And Bash - Palindromes Revisited
Hey There,
As the clever title of today's post suggests, this is the follow up to our post on scripting out a way to determine if a string is a palindrome using Perl.
One of the main things to note (which isn't the only difference, of course) is the way in which the Bash shell and Perl deal differently with "routines." In our Perl script, we did the meat of our recursive work inside a "subroutine." In today's Bash script, that same work is handled inside a "function."
Functionally speaking, both a Perl "subroutine" (Identified by the "sub" declaration) and a Bash "function" (Identified by the "function" declaration) do the same things. They allow you to create a block of code for use within in your script. Naturally, functions and/or subroutines, are a great help if you find that your script consists of typing the same set of instructions more than once (if you have to type the same block of code more than twice, they're even better ;)
Technically speaking, there are some major differences between the two that should be noted. These should be platform independent and true for both Bash and Perl on Linux or Unix.
In Perl, since all of the information in the script is processed before the script is run, you can include your "subroutine" anywhere in the script, even if it's after the line on which you call it. It's common practice to put subroutines at the bottom of the script, but you can put them anywhere within the script that you like, if you're so inclined.
In Bash, since the script is parsed from top-to-bottom in a left-to-right fashion, "functions" absolutely need to appear in the script before the line on which they are called. If you put a function definition at the bottom of your Bash shell script and call that function 15 lines prior, you'll receive something along the order of a "command not found" error.
In Perl, when you pass arguments to a subroutine, you have a few different ways you can do it (basically, deprecated methods still work), but the most elegant way to pass simple variables to your Perl subroutine is to include them within parentheses, separated by commas, like so:
MySub( $var1, $var2, $var3);
The subroutine that would accept, and process, all of those arguments may look something like this:sub MySub( $var1, $var2, $var3) {
print "$var1 $var2 $var3\n";
}
In Bash, when you pass arguments to a function, you can just pass them as if they were arguments to a regular command, like this:
MyFunction $var1 $var2 $var3
The function that would accept, and process, these arguments may look something like this:function MyFunction {
echo "$var1 $var2 $var3"
}
Interesting side note: In Bash, if you declare a function, you can leave out the reserved word "function" if you want to, but this requires that you use parentheses, like with Perl. So you could write your function like this:MyFunction () {
echo "$var1 $var2 $var3"
}
However, if you opt to use the reserved word "function" when declaring your function (like we do), the parentheses following the function name are optional :)
You may have noted that we haven't mentioned anything about "scope" with regards to variables and functions or subroutines. This is on purpose. We'll inevitably tackle that in a later post, but it's beyond the "scope" of this one (that was a truly painful pun for all of us... but utterly unavoidable ;)
On that note (for the palindromes), in our Bash script we've taken advantage of that "lack of scope" and simply set the "$status" variable globally within the palindrome function. In our Perl script, we used "return" to pass back either a 0 or a 1 to the calling procedure. The major difference here is that our Perl script was returning a code back to a variable which was defining its value based on the outcome of the subroutine process, so:
$status = palindrome( @string, $chars, $count );
was giving the "$status" variable a value of 0 or 1, depending on what the "palindrome" subroutine returned. In Bash, we jumped right over the middle man and just set the global "$status" variable from within the function (This isn't generally recommended, but okay for our purposes here). Assuming there are no bugs in your version of Bash that mangle the scope of variables within functions, the only risk you're taking, by defining a global variable from within a function, is that you'll forget about it and redefine it outside the function, thereby overwriting the value. But, again, that's for another day, as I can feel an essay coming on ;)
Hope you enjoy the Bash version of our simple palindrome script and that it helps you see the correlation between not only functions and subroutines, but some other aspects of coding that are either common, or unique, to both Bash and Perl.
Best wishes,
Cheers,
This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License#!/bin/bash
#
# bashpal.sh
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
function palindrome {
if [ "${string:${count}:1}" == "${string:${chars}-${count}-1:1}" ]
then
let count=$count+1;
palindrome "$string" "$chars" "$count"
fi
if [ $count -eq $chars ]
then
status=1;
else
status=0;
fi
}
printf "Enter A String: "
read string
chars=${#string}
count=0
palindrome "$string" "$chars" "$count"
if [ $status -eq 1 ]
then
printf "\n That String Is A Palindrome.\n\n"
else
printf "\n That String Is Not A Palindrome.\n\n"
fi
, Mike
linux unix internet technology





