Showing posts with label write. Show all posts
Showing posts with label write. Show all posts

Wednesday, April 29, 2009

/dev/null And /dev/zero On Linux And Unix: What's The Difference?

Hey there,

Today we're going to take a look at a relatively simple concept that's relatively simple to get confused; especially if you're relatively new to Unix or Linux. Your experience may vary. It's all relative (who didn't see that one coming from around the corner and down the block? ;)

The concept we'll be looking at is the dead-zone on Linux or Unix. Most people refer to it as /dev/null or the "bit bucket." And, of course, this prompts the question of "why is that so interesting?" I suppose, in and of itself, it's not. Don't get too excited ;)

Where it gets interesting is when we introduce the /dev/zero file (technically, like /dev/null, a pseudo-file). Most folks, at least initially, consider /dev/null and /dev/zero to be virtually equivalent. And, even though the names make that conclusion seem self-evident, the truth is actually quite the opposite. The two files differ quite drastically, although not in every aspect.

1. Writing to /dev/null and /dev/zero: You can write to both /dev/null and /dev/zero in exactly the same way. For instance, if you want to dump off output from a command, either one will do. That is to say that:

host # echo hi >/dev/null

and

host # echo hi >/dev/zero

will both send your output to "never never land." Executing either of the above commands will satisfy your requirements if you just want to "dump" output to "nowhere." They should both be character (or raw) devices, have identical major device numbers and only differ at the minor device number level. These numbers will differ from OS to OS, but the basic definitions above should hold relatively true.

2. Reading from /dev/null and /dev/zero: This is where the difference between the two files becomes apparent. The most significant difference is exposed in the "reading" since this action highlights the major way in which the two differ.

/dev/null is, essentially, a black hole. Writes to it (as noted above), basically go down the drain. They go nowhere, stay there and you can't get them back. When you "read" from /dev/null, the same rule holds true. /dev/null is virtually "nothing," and all reads from it produce no output whatsoever. For instance, this output from Solaris' truss (you can get the same from Linux's "strace" and similar utilities) shows what happens when /dev/null is read from (e.g. "cat /dev/null") - below, what you'd see at the command line, followed by a snippet of truss output from the almost-immediate end of the command's execution:

host # cat /dev/null
host #
host # truss cat /dev/null
...
open64("/dev/null", O_RDONLY) = 3
fstat64(3, 0xFFBFF950) = 0
read(3, 0x000222A0, 8192) = 0
llseek(3, 0, SEEK_CUR) = 0
close(3) = 0
close(1) = 0
_exit(0)
host #


literally, the device file is opened, read from (which produces nothing) and is closed, since an EOF is sent immediately after opening.

/dev/zero, on the other hand is not the black hole that it appears to be when "writing to it." When you "read" from /dev/zero, you get a much different result than when you read from /dev/null. This is most specifically because /dev/zero returns zero's until the cows come home (or you stop reading from it ;) and "does not" return an EOF like /dev/null. It actually returns the ASCII null character (0x00) ad infinitum (or "ad naseum" depending upon your stomach for this sort of thing ;)

Below, we'll take a look at what you'd see at the command line, followed by a snippet of truss output from this command's execution:

host # cat /dev/zero
^C
host #
<-- note that we had to type ctl-C (send an interrupt signal) to the return from "cat /dev/zero" to get it to return to the shell prompt
host # truss cat /dev/zero
...
open64("/dev/zero", O_RDONLY) = 3
fstat64(3, 0xFFBFF950) = 0
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0".., 8192) = 8192
write(1, "\0\0\0\0\0\0\0\0\0\0\0\0".., 8192) = 8192
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0".., 8192) = 8192
write(1, "\0\0\0\0\0\0\0\0\0\0\0\0".., 8192) = 8192
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0".., 8192) = 8192


...and on it goes until we (or another process) interrupts it.

And that, pretty basically, is the difference between the two files. It should be noted, however, that there are two things you can do with /dev/zero that you can't do with /dev/null (one practical and one just downright malicious if you know what you're doing ;) and one very useful thing you can use /dev/null for on a regular basis.

1. /dev/null can be easily used to create a zero byte file. This comes in handy when you want to create a placeholder file, etc:

host # cat /dev/null >FILE
host # ls -l FILE
host # ls -l FILE
-rw-r--r-- 1 user123 unixgrp 0 Apr 28 11:37 FILE


of course, the same results can be achieved (in most shells) in a more efficient manner. The underlying mechanism is the same, but the typing's a bit speedier (and you're actually redirecting STDOUT, but we'll leave that distinction to the academics ;)

host # >FILE
host # ls -l FILE
-rw-r--r-- 1 user123 unixgrp 0 Apr 28 11:37 FILE


2. With /dev/zero, the good thing you can do is to create a zeroed out file on which you can, later, create a filesystem. Using a simple command like "dd" you can set /dev/zero as your input file ("if") and your block device (or regular file) as the output file ("of") and then make a filesystem on it. This is a very quick way to setup additional file system space:

host # dd if=/dev/zero of=mynewfile <-- the block size - "bs" - and count - "count" - arguments will differ depending upon your fs and what your particular needs are
host # mkfs ARGS mynewfile <-- Again, this part (creating the new filesystem) can have many variables. The most relevant command line component, in this instance, would be the name of the zeroed out file we create with "dd."

3. With /dev/zero, the "bad" thing you can do (or can do by mistake, if you're not careful or aren't aware of /dev/zero's behaviour) is related to the "good" thing. Just like you can create a zeroed out file system, you can also completely fill a partition with ASCII 0x00 characters and, possibly, cause your system to crash or go into a frenzy as it loses disk resources, like so (of course, you should not have the permission to do this as a regular user):

host # cd /var
host # cat /dev/zero >FILE
cat: output error (0/8192 characters written)
No space left on device
host # df -k /var
Filesystem kbytes used avail capacity Mounted on
/dev/dsk/c0t0d0s4 4130238 4120138 0 100% /var


so, in about 100 seconds, you can cripple /var (4 GB of space on this host). That makes it difficult to do simple things, like copying over a relatively small file onto the partition you just filled up (not to mention the other adverse effects filling up /var can have on most Linux and Unix OS's):

host # ls -l /bin/ls
-r-xr-xr-x 1 root bin 27380 Jun 11 2008 /bin/ls
host # cp /bin/ls /var/ls
cp: /var/ls: write: No space left on device


Of course, this practice is not recommended. Just a small example to illustrate why you should take care when playing around with /dev/zero!

Here's hoping today's post was of some help to you or, at the very least, provided an interesting diversion for a few moments ;)

Cheers,

, Mike




Discover the Free Ebook that shows you how to make 100% commissions on ClickBank!



Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Monday, July 7, 2008

Variable MultiLine Spacing With Sed On Linux Or Unix

Hey There,

Today's post is going to be on a relatively basic subject that can elude a lot of folks who use sed casually. I, myself, for years, only used it to print out matched patterns or do pattern substitutions, but it's capable of much more than that. I don't think it's quite up there with awk (insofar as the programming functionality goes), but it does have lots of neat features (like letting you switch between the current and hold buffer, for example) that can sometimes make it the preferred tool for the job. Especially if you're like me and you're not attached to any one language or interpreter. My basic philosophy is: Whatever can get it done the fastest and most efficiently. I think it works nicely, as philosophies go, since I also compulsively automate everything ;)

For this post, we'll focus on "line spacing" (If anyone calls it something else, I'm referring to the number of spaces between lines). This is one thing that sed can do relatively simply and which is requested quite often from end users. The following tips should work for both Unix and Gnu/Linux sed.

The great thing about this feature is that it's so easy to use that, if you haven't used it already, you'll be amazed at the simplicity :)

Say, for instance, you had a file named FILE with the contents:

abcdef
ghijkl


this is all you would have to do with sed to make it double-spaced (Note that I'm leaving the output redirection to another file, since it's not really germane to the explanation and you probably already know how to redirect the output of one process to another. And, since this blog is for users of all skill levels, check out any of these old posts on using sed in a wide variety of situations if you don't. See below for more information on this feature:

host # sed G FILE
abcdef

ghijkl

# host


Pretty simple, yeah? :) The "G" argument to sed simply appends whatever follows it to the pattern space that gets printed out. Since we didn't have anything to print out, it prints a space by default.

Now, if you want to triple space, it's just a little more complicated, but not much. Here's how you'd do that

host # sed 'G;G' FILE
abcdef


ghijkl


#host


Note that we had to put the arguments in single quotes, since there are (of course) more than one ;) We could have written the last command as:

host # sed 'G' FILE

and gotten the same result as without the quotes, since there was only one argument to sed. If you want to quadruple-space, just use 3 G's, etc. That's about as simple as it gets on a single command line without control characters :)

It's also interesting to note (and overlooked in this old post on using Perl like sed) that you can use "r" and "w" to read from, and write to, files so you don't need to do any redirection outside of your one-liner.

For instance, if you wanted to read information in from another file (FILE2 with contents "xyz") and double space, you could type:

host # sed 'G;r FILE2' <FILE
abcdef

xyz
ghijkl

xyz
host #


Not exactly the same, but pretty useful in different situations.

Also, you can write out to a file, using w, so you don't have to do output redirection after-the-fact, like this (we'll overwrite the FILE2 file with the contents of FILE):

host # sed 'G;w FILE2' <FILE
abcdef

ghijkl

host # cat FILE2
abcdef

ghijkl

host #


Hope you enjoyed today's post and it gets you a little more interested in sed. We'll be looking at in the future, of course, as there are a lot more things you can do with it (especially if you escape the command-line construct)

Cheers,


, Mike

Wednesday, April 2, 2008

Using Bash To Access The Network Via File Descriptors

Greetings,

It's been a while since we looked at down-and-dirty shell tricks, and this one comes as a tangential follow-up to our previous post on finding and reading files in the shell when your system is on the verge of a crash.

The first thing I'd like to clear up before we proceed is that, in re-reading that post, I notice that I was being a bit of a ksh-snob in the section regarding reading the contents of a regular file through a file descriptor to save on resource usage. My example line in that post, after exec'ing file descriptor 7, was:

host # while read -u7 line; do echo $line; done

And, as most bash fans probably noted, that syntax doesn't work in bash. That's what I get for only verifying the output in ksh ;) What that line should have read (since this will work in sh, ksh and bash) was:

host # while read line;do echo $line;done <&7

Apologies all around. In any event, that "mistake" serves as a fairly decent segue into what we're going to be looking at today. That same basic process of exec'ing file descriptors to read files directly is how we're going to use bash to read from the network directly using /dev/tcp.

And, just to be clear, this "does not" work in Solaris ksh or sh. It will work on Solaris and every Linux I've tested, but only, to my knowledge, in bash. Every version of ksh I've tested (even Solaris 10) can't create the virtual file system required by this operation. I've attached a small bash script, at the end of the post, to check a mail server and http server, so you have a working example of the process.

This method of accessing the network is very useful to know how to do and, going through it step by step, is actually very simple to accomplish. We'll use a mail server as our example since it involves both reading and writing to the network file descriptor.

The first thing you'll want to do is "exec" the file descriptor you'll be using to communicate on the network via tcp. You can do this like so (Just be sure not to exec file descriptors 0, 1 or 2, as these should already be assigned by your operating system as "Standard Input (STDIN)," "Standard Output (STDOUT)," and "Standard Error (STDERR)" :

host # exec 9<>/dev/tcp/mail_server.xyz.com/25 <-- Here we create file descriptor 9 via exec, open the /dev/tcp/mail_server.xyz.com/25 "virtual file" and assign file descriptor 9 to it.

Note that this line is where the script, or process, will either make or break. ksh (Definitely for Solaris 9 and 10) does not allow the creation of the virtual filesystem below /dev/tcp that this operation requires. If you attempt to do this in ksh you will most likely get the following error:

host # /dev/tcp/mail_server.xyz.com/25: cannot create

Assuming we're using bash, and that worked okay, we can now write to, and read from, our mailserver over port 25. This is analogous to doing something like Telnetting to port 25, but we're using a built in feature of the bash shell to access the network via a pseudo file system created under /dev/tcp (It acts much like a /proc filesystem, although you can't see what you've created by looking at the file that /dev/tcp links to, since it's a character device file and the directory path /dev/tcp/mail_server.xyz.com/25 doesn't "really" exist :)

Now we can send input, and receive output, using standard file descriptor redirection commands. A few "for instances" below:

host # echo "HELO xyz.com" >&9 <--- This sends the HELO string to our mailserver via file descriptor 9, which we exec'ed above.
host # read -r RESPONSE <&9 <--- This reads the data coming in on file descriptor 9 with as little interpretation as possible (for instance, it treats the backslash (\) as a backslash and doesn't imbue it with it's usual "special" shell powers).
host # echo -n "The mail server responded to our greeting with this load of sass: "
host # echo $RESPONSE


And, now, we can write to, and read from, file descriptor 9 until we have nothing left to say ;) When we're all finished, I find it's good practice to close the input and output channels for our file descriptor (although closing the input should be sufficient in most cases), and, if you want to or need to, you can also dump the remainder of the file descriptor's contents before you close it down.

host # cat <&9 2>&1 <--- Dump all of the remaining input/output left on file descriptor 9 to our screen.
host # 9>&- <--- Close the output file descriptor
host # 9<&- <--- Close the input file descriptor

Now, you're back to the way things were and you shouldn't be able to read or write from file descriptor 9 anymore (or, if you're a glass-half-full person, it's now available for use again :) I've tacked on a small script to demonstrate some basic functionality, but, hopefully, you'll have some fun playing around with this and figure out more, and better, ways to manipulate your server's interaction with the network through the file system.

host # ./check_net.sh <--- As simple as that to run it :)

Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/bash
#
# check_net.sh
#
# 2008 - Mike Golvach - eggi@comcast.net
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

mailserver="mailserver.xyz.com"
httpserver="www.xyz.com"
domain="xyz.com"

echo "Testing mail server functionality"
exec 9<>/dev/tcp/$mailserver/25
read -r server_version <&9
echo "Server reports it is: $server_version"
echo "HELO $domain" >&9
read -r greeting <&9
echo "Server responded to our hello with: $greeting"
echo "VRFY username" >&9
read -r vrfy_ok <&9
echo "Server indicates that this is how it feels about the VRFY command: $vrfy_ok"
echo "quit" >&9
read -r salutation <&9
echo "Server signed off with: $salutation"
echo "Dumping any remaining data in the file descriptor"
cat <&9 2>&1
echo "Closing input and output channels for the file descriptor"
9>&-
9<&-
echo "--------------------------------------------------"
echo "Testing web server functionality - Here it comes..."
exec 9<>/dev/tcp/$httpserver/80
echo "GET / HTTP/1.0" >&9
echo "" >&9
while read line
do
echo "$line"
done <&9
echo "Dumping any remaining data in the file descriptor"
cat <&9 2>&1
echo "Closing input and output channels for the file descriptor"
9>&-
9<&-
echo "done"


, Mike




Friday, December 7, 2007

The Shebang Line: An Introduction to Porting Shell to Perl

This is actually going to be, over time, a series of posts because it would be impossible (especially with my writing style) to cover every aspect of porting shell script to Perl in one posting (There may be some unusual or vague aspects that won't be covered at all, but, by that point, you will understand enough to be able to find the answers through trial and error, or judicious use of various search engines ;)

In this post we're going to cover one of the very basics. By "very basic," I mean a brief discourse on the "shebang" line. There's so much to mine in that seemingly obvious topic that it'll take far too long for me to crank out anything more today. After this, in a future post, we'll follow up with how to properly format a standard line of code, the three common variable types and how they are created and accessed in either language.

The beginning is complicated enough, when you really look at it. The "shebang" line (It's referred to as this because the exclamation point is referred as a "bang," for reasons which I don't completely understand, and the pound symbol is... I honestly can't come up with any intelligent reason for the other part of the name ;). I still call it the "pound bang" line.

In shell and in Perl, the "shebang" lines serve the same purpose. In fact, the "shebang" line is an entity unto itself. Its use extends beyond shell and/or Perl scripting and is a construct used for many purposes. The main thing the "shebang" line does is instruct the shell that you're using (you can be logged into csh, tcsh, ksh, sh, zsh or whatever shell you like to work in) to invoke a specific "command" to initiate the script with. It is customary to make this the full path to the shell in which you are writing your script, or the full path to the Perl binary, but it doesn't have to be (although, what you are asking the "shebang" to execute should be directly connected to it. No space between "#!" and "/bin/sh." Lots of shells nowadays will consume this blank space for you. Some older versions of shells are far less forgiving). For instance, our two basic examples are these:

#!/bin/sh <--- Tells the shell's interpreter to invoke "/bin/sh" in a subshell and run all the commands in the script in that subshell.
#!/usr/bin/perl <--- Tells the shell's interpreter to invoke "/usr/bin/perl" in a subshell and run all the commands in the script in that subshell.

This doesn't mean you can only use one shell or even one version of Perl within the same script, but that's outside the scope of today's post. So, to illustrate what I mean when I say the "shebang" line instructs the shell to invoke a specific "command," I mean just that. If you wrote a script, in the editor of your choice, and made the first line:

#!/bin/rm

the default action of the shell would be to delete the file and then run all the following commands (reading the script - in both shell and Perl - left to right, from top to bottom) until the script ended. If this seems counterintuitive, which it did to me at one point, that's normal. This is a good example of what goes on "under the covers" when you execute a script in your Unix or Linux shell. In the above example (as in the previous two, and all others), the entire script is read into memory before the "shebang" line is executed. That way, when your script with "#!/bin/rm" at the top is executed, the following happens:

1. The script is read into memory.
2. The script is executed.
3. The first line ("shebang") instructs the script to delete itself.
4. The rest of the script is run out from memory.
5. When the script is done, everything you instructed the script to do has been completed (provided it's at all possible in the shell you're executing it from (since the default subshell will be a subshell of the shell you're in when you execute the script) and the script is no longer there! In actuality, the script disappears before the code is executed from memory, so your script is gone, sometimes, long before your script finishes! For example, let's look at the following script:


#!/bin/rm
# /export/home/user/MyScript.sh c) 2007 MT - xyz.com
#
# Pounds are considered comment lines in both shell and Perl
# You can include interesting notes or whatever you like behind
# the pound sign and the script won't execute it. Even the "bang"
# ! has no special significance after the first line of the script
# except in special situations where you're invoking another
# subshell or another "shebang."
# Blank lines are also not acted upon in either shell or Perl.

sleep 500000000000000000


Now, if we were to run that script at the command line (after doing a chmod to 700 so that we, the user, have full read, write and execute permissions on it) we would end up sitting at a hung prompt, waiting for the sleep command to finish for a very long time. In this case that's good, because it gives us ample opportunity to login to the same machine again, on a different terminal, and have a look at what's going on:

This is an example of what our first terminal session would look like:

user# cd /export/home/user
user# ls -a
. .. .profile MyScript.sh
user# /bin/bash ./MyScript.sh
user#


Now, if we log on to the machine again, using a different terminal (or another connection, window; however you want to put it) we'd see this:

user# cd /export/home/user
user# ls -a
. .. .profile
user#


Yet - we can check that the script is still running, which it is:

user# ps -ef|grep "[M]yScript.sh
user# user 6523 6521 0 15:12:03 ? 0:14 ./MyScript.sh


And thus is all that explained. You can use the "shebang" line to run any command you want. Try it out. Be careful, because you can do some incredibly awful things if you're not careful. Luckily, the shell will protect you against doing something like this (but the way it reacts is specific to the command invoked, so some programs, like Perl, can accept a switch after the command line, like - #!/usr/bin/perl -w):

#!/bin/rm -rf /export/home/user

as it only executes the full qualified "rm" command directly attached to the "shebang." If you quote the entire command to try and get around it that way, it will try to interpret the entire line as one command and shoot you an error like:

ksh ./MyScript.sh: not found

Hopefully, this has been a somewhat informative and easy to understand introduction to the relationship between the shell and Perl. I understand that this bedrock we've discussed today isn't really specific to either of them, although it applies to both. But, as an old saying goes: It's usually best to start with the first floor (or the basement) when you're putting up a building ;)

Best Wishes,

, Mike



Thanks to Michael Pelletier, Merrimack NH for this perfect explanation of what that pound symbol means!


It's a "sharp" symbol in music notation:

http://en.wikipedia.org/wiki/Sharp_(music)

That's why the "C#" language is pronounced "C sharp," not "C plus plus plus plus:"

http://en.wikipedia.org/wiki/C_Sharp_(programming_language)

Enjoy this little tidbit of mostly useless knowledge. I pronounced it "pound bang" for maybe 20 years.









Wednesday, December 5, 2007

Converting Alpha Permissions To Octal

Today, I thought I'd put together a little script (written in ksh, but easily portable to sh or Perl) to address a common concern amongst users of most Unix/Linux-based systems; how to translate alpha permissions (like -rwxrwxr-x) to octal (like 775). This issue has been addressed, to some degree by all flavors of *nix, in that you can use the chmod command either way. For instance, if you had a file with 775 (or -rwxrwxr-x) permissions, you could add "other" (or "world") write permissions to the file by doing:

chmod 777 FILENAME or chmod o+w FILENAME

Both ways are equally effective and, you might even say that their are some advantages to using one over the other. For instance, with octal permissions, to add "group" write permissions to a 755 file, you would have to remember to retain the original modes when executing chmod. If you just did:

chmod 070

It would cause serious issues (755 becomes 070)! It's much easier to remember:

chmod g+w

Which would add the write permissions for "group" ownership and not affect the other bits (755 becomes 775)!

One example of a situation where you absolutely "must" use alpha notation with chmod is an old bug in Solaris (that exists even today, in Solaris 10 - latest patch release), whereby you "cannot" set the "group ID" bit (more commonly referred to as setgid) on a directory with octal notation. For instance:

xyx.com[/export/home/user] # chmod 2755 bob
xyz.com[/export/home/user] # ls -ld bob
drwxr-xr-x 2 root other 512 Dec 3 16:34 bob
<--- No Good.

xyz.com[/export/home/user] # chmod g+s bob
xyz.com[/export/home/user] # ls -ld bob
drwxr-sr-x 2 root other 512 Dec 3 16:34 bob
<--- Now it's working :)

The only other thing left to explain before we get to the script (assuming you're still reading this and haven't just blown to the bottom already ;) is the common misconception that there are only 3 octal numerics in a file's (or anything's) permissions. This may not be news to most folks, but there are actually 4 octals. The last 3 are the ones that are usually represented; as in 755. The fourth, which is actually the first, is usually an implied 0. For example, 755 is the same as 0755. The first (usually omitted) bit has a potential of 4 different values (0, 1, 2 and 4 or any combination thereof), just like the rest of them do, although they take on a unique significance.

For the last 3 bits (user, group and other permissions) the translation is the same. They can have values of 0-7 for each. 000 would equal ---------, while 777 would equal rwxrwxrwx The permissions are compounded by adding the value of the bits. So 0 results in a "---", 1 results in "--x", 2 results in "-w-" and 4 results in "r--". Compounding them is just a matter of addition. So if the value of the user bit was 6, you could only acheive that result (from your selection of 0, 1, 2 and 4) by adding 4 and 2. By merging the above descriptions, 6 would have a value of "rw-" (4 "r--" plus 2 "-w-").

The first (usually hidden) bit is reserved for activating the setuid bit (value of 4), setgid bit (value of 2) and sticky bit (value of 1). As always, 0 equals nothing. These bits are special because, although they'll appear as numerals in your 4 bit octal notation, they appear in different places when using alpha notation. The setuid bit (Value of 4) shows up in the "user" permissions as an "s" where the "x" would normally be (like rws instead of rwx). The same is true of the setgid bit (Value of 2), although it shows up in the "group" permissions. The sticky bit (Value of 1) shows up in the "other" permissions as a "t" (like rwt instead of rwx). In a future post, we'll explore how the bit permissions "actually" modify the files and/or directories they are applied to (This differs for all 4 bits, and is another post in itself).

Below is the initial script to convert all of the alpha permissions you'd see (using "ls -l") on any file(s), to their corresponding octal notation. This can more easily be applied in reverse (which is probably why I wrote it this way first). Note that this was also created using what I call "brute force scripting"; the fastest means to an acceptable end. Feel free to doll it up and make it work better for you. As it is, it will accept any option that the standard "ls" would. So, if you called it "lso," you could execute "./lso /export/home/user" or "./lso -R /export/home/user" or just plain "./lso" -- I'll be using this script (since it has a number of different scripting constructs in it) to demonstrate how to port shell scripts to Perl in a future post, also.

Best wishes :)


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/ksh

#
# 2007 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

arg=$@

ls -l $arg|awk '{ if ($0 !~ /^$/) print $1 " " $NF}'|grep -v "^total"|while read perms filename
do
if [ $filename = $perms ]
then
print "$filename = Directory"
continue
fi
bit_counter=10
oct_counter=0
user_bits=0
group_bits=0
other_bits=0
s_bits=0
set -A oct_array
oct_split=`print -- $perms|sed 's/\([-bcdDlprstwx]\)/\1 /g'`
for x in `print -- $oct_split`
do
oct_array[$oct_counter]=$x
let oct_counter=$oct_counter+1
done
while [ $bit_counter -gt 0 ]
do
case $bit_counter in
'9' ) if [ "${oct_array[$bit_counter]}" = "x" ]
then
let other_bits=$other_bits+1
elif [ "${oct_array[$bit_counter]}" = "t" ]
then
let s_bits=$s_bits+1
let other_bits=$other_bits+1
fi
let bit_counter=$bit_counter-1;;
'8' ) if [ "${oct_array[$bit_counter]}" = "w" ]
then
let other_bits=$other_bits+2
fi
let bit_counter=$bit_counter-1;;
'7' ) if [ "${oct_array[$bit_counter]}" = "r" ]
then
let other_bits=$other_bits+4
fi
let bit_counter=$bit_counter-1;;
'6' ) if [ "${oct_array[$bit_counter]}" = "x" ]
then
let group_bits=$group_bits+1
elif [ "${oct_array[$bit_counter]}" = "s" ]
then
let s_bits=$s_bits+2
let group_bits=$group_bits+1
fi
let bit_counter=$bit_counter-1;;
'5' ) if [ "${oct_array[$bit_counter]}" = "w" ]
then
let group_bits=$group_bits+2
fi
let bit_counter=$bit_counter-1;;
'4' ) if [ "${oct_array[$bit_counter]}" = "r" ]
then
let group_bits=$group_bits+4
fi
let bit_counter=$bit_counter-1;;
'3' ) if [ "${oct_array[$bit_counter]}" = "x" ]
then
let user_bits=$user_bits+1
elif [ "${oct_array[$bit_counter]}" = "s" ]
then
let s_bits=$s_bits+4
let user_bits=$user_bits+1
fi
let bit_counter=$bit_counter-1;;
'2' ) if [ "${oct_array[$bit_counter]}" = "w" ]
then
let user_bits=$user_bits+2
fi
let bit_counter=$bit_counter-1;;
'1' ) if [ "${oct_array[$bit_counter]}" = "r" ]
then
let user_bits=$user_bits+4
fi
let bit_counter=$bit_counter-1;;
0 ) let bit_counter=$bit_counter-1;;
* ) let bit_counter=$bit_counter-1;;
esac
done
octal=${s_bits}${user_bits}${group_bits}${other_bits}
print -- "$octal $filename"
done


, Mike