![]() |
Command line fun
OK, I'm obviously a bit bored at the moment, so I dusted off an old script that I had lying around and cleaned it up a bit. [If anyone has a copy of the old version, please trash it; this one's far sounder.] It's modelled on an old utility that used to run on some SunOS machine, but ceased working when my ex-workplace upgraded to Solaris some time back. Someone who had grown reliant on this executable wanted it back, so I thought it would be fun to write it in perl. Hmm, so what *is* it? The original was called "lc", and is a variant of "ls", except that it separates files from directories and displays the results "across" the page instead of in columns.
I'm a fan of the separation aspect, but the row-based display is a bit awkward to view, so I've written my replacement to do column-based output by default, with row-based output available via a flag. The other big thing I've added is case insensitive sorting (by default). I think this makes a lot of sense, particularly when used on a mac. Anyway, enough rambling: you should be able to just copy the script below (between the %%%%% lines), then open up a terminal window and enter % cat > ~/bin/lc [[Now type command-V to paste in the copied contents]] [[Now hit return and then control-D]] % chmod u+x !$ % rehash And that should be it. Instant "lc" goodness. Enter "lc -h" for usage instructions. The defaults should work OK. I have a couple of aliases set up: nothing fancy, but in my .cshrc there's alias lcd 'lc -d' alias lcf 'lc -f' alias lcl 'lc | less' Cheers, Paul %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #!/usr/bin/perl -w use strict; use Getopt::Std; use vars qw($opt_f $opt_h $opt_d $opt_s $opt_r $opt_l); my $screenwidth = 80; # adjust if necessary my $dirs_topstring = "+----------------- Directories "; #adjust as desired! my $files_topstring = "+--------------------- Files "; getopts('dfshrl'); # These are the valid options: see help message if (!($opt_f || $opt_d || $opt_h)){ $opt_f=1; $opt_d=1 }; # show both if neither option present (and it's not a help request) warn <<HELP if $opt_h; A bare "lc" will separate files and directories, and sort them in a case insensitive manner down the screen (ie in columns). To sort case sensitively, or to display results in rows, or to show just files or just directories use the flags as outlined below. Flags may be grouped together (so "lc -id" is the same as "lc -i -d") -------------------------------------------------------------------------------- -d : Show directories -f : Show files (note that a bare "lc" will show both) -s : Sort case sensitively (eg "Lemon" before "apple") -r : List the output in rows instead of columns -h : Display this message -l : Display link (@) and exec (*) status on files displayed HELP open LS,"ls -F|"; # pipe the output of ls -F to this handle my @what = <LS>; # catch the output from the ls -F command my @dirs = grep /\/$/,@what; # directories end with "/" my @files = grep /[^\/]\n$/,@what; # the rest (incl. links) if (@dirs && $opt_d){ # process dirs, if they exist and are requested my @sdirs = map {substr $_,0,-2} @dirs; # kill / and newline at once bannerprint($dirs_topstring); displaylist(\@sdirs); } if (@files && $opt_f){ # show files, if they exist and are requested chomp @files; $opt_l or s/\*|\@$// foreach (@files); # remove exec/link status? bannerprint($files_topstring); displaylist(\@files); } ### Subroutines used above ### sub bannerprint{ my $starter = shift; my $topstring = $starter.("-"x($screenwidth-length($starter)-1))."+\n"; print $topstring; } sub displaylist{ my $array_ref = shift; my $disp_len = getlen($array_ref)+1; my $num_disp_perline = int(80/${disp_len}); my @list = @{$array_ref}; @list = sort {lc($a) cmp lc($b)} @list unless $opt_s; if ($opt_r){ # for some perverted reason they want rows... OK!! my $j=0; foreach (@list){ printf "%-${disp_len}s", $_; $j++; print "\n" if $j % ${num_disp_perline} == 0; } print "\n"; }else{ # here's one revolting way to print out columns my $length = @list; my $numrows = int($length/$num_disp_perline); $numrows++ if ($length/$num_disp_perline - $numrows); # extra row if doesn't divide evenly (ie there's a remainder) foreach my $i (1..$numrows){ my $j=0; my @row = grep {$j++;($i-$j)%$numrows == 0} @list; printf "%-${disp_len}s",$_ foreach @row; print "\n"; } } } sub getlen{ my $arr_ref = shift; my $blen = 0; my $len = 0; foreach (@{$arr_ref}){ $len = length($_); $blen = $len if $len>$blen; } $blen++; # return one greater than the longest word } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
This is all that I got when I tried running it...
Quote:
|
holy schnikeys! hey paul. an excellent sample.
to get a copy/paste of this to run, you'll have to delete the space after the standalone HELP around line 27. |
Thanks merv!
Works like a charm! I like very much! |
pmccann,
The pirate from the southern seas throwing another "gem" to the multitude. Paul, it works very well, thanks a lot! Cheers... PS: Could you guide me to 'update for iTunes3' the script you share with us at the end of this thread?: http://forums.macosxhints.com/showth...n&pagenumber=1 |
Thanks pmccann - works great!
|
Hi again,
thanks for the pick-up Merv: seems to be a browser-dependent thing, as I *swear* I tested cut/paste with IE. Maybe I'm the only one still using this thing! Damn, damn, damn: just checked again and there's that extra space problem in IE, so I suck. Wacko stuff: it's not evident in the selection shadow in the browser window, but comes running out of hiding once you paste. Indeed, every line gets that "breathing space" at the end after pasting. (Maybe it's *only* IE that does this? Yes and no: just checked with Chimera, and it seems to highlight an extra space in the browser window, but then pastes without it. Sigh...) The extra space thing does ring a bell though, --doubtless some other "heredoc" has triggered the difficulty-- so I'll have to watch that in the future. sao, what's the problem with iTunes3? I don't have this beasty, and don't want to put my poor old modem through that particular hell, but if you want to send me (ie email) a (small) portion of the export file I'm willing to have a look. Oh yeah, someone has started a perl module to look directly at the itunes library files. Have a look at search.cpan.org if you're interested. Hey! It's the *brand new* search.cpan.org: very cool indeed. The itunes stuff is at: http://search.cpan.org/author/BDFOY/...iTunes-0.5_01/ Cheers, Paul |
pmccann,
Very nice script! I modified it a little to support the -a option (lists all files) and listing of other directories (like "lc /Users". Is it OK with you that I post it? Erik |
Hi Erik,
Sure, do with it what you will. Sounds like things that I should have had in there from the start! Cheers, Paul |
Quote:
Code:
[SIZE=small]#!/usr/bin/perl -wErik |
Newbie raises hand from the back of the room...
Unix beginner here...
% cat > ~/bin/lc returns /Users/vicki/bin/lc: No such file or directory. I know how to create the dirs, but I don't know if I should because my perl scripts are in my /u/web/vickis/local-cgi/ subdir (path matches my online webhost). I know how to create the file in my local-cgi dir and chmod it. What I don't know is: 1) I have my httpd.conf set up to use the extension .pl for my scripts. Do I name the file lc.pl? 2) How do I execute the script from the terminal? And how do I add my local-cgi dir to <insert whatever kind of> path to make it executable no matter what dir I'm in while in the terminal? Or in other words... can someone take me from what I know to what I need to know to run the lc script while in any subdir in the terminal? All I know is to plop scripts with the extension of .pl into my (/u/web/vickis/local-cgi/) dir and exectute them from my browser (http://localhost/cgi-local/lc.pl). I don't think that's the intended usage of lc. ;) |
On second thought... I'd really prefer to have two subdirs in which I can execute perl scripts. I'd like to have (1) for my website scripts and (2) for my own personal use on my Mac. Can I do this? If so, how?
|
Quote:
Edit: The reason Paul's intructions didn't work is that you don't have a ~/bin directory. Just create it and you will not get that error message. Erik |
Thanks, Erik! I got it to work. :)
But why is it not showing the file, dbz118a.jpg? Output I receive using my ls2 (an ls modified alias): Code:
[11:20am] [vicki] [~/Pictures] Exit, and we all die: % ls2Code:
[11:20am] [vicki] [~/Pictures] Exit, and we all die: % lc Edited to add: I also don't understand the Icon? reference. Here's what the Finder shows in that dir: Code:
dbz118a.jpg |
Quote:
rm Icon[tab-completion] |
Thanks, Merv! That did the trick. lc is working correctly now. :)
Although I remain puzzled as to what that Icon^M file was and how it got there. I wonder how many other Icon^M files I might have hanging around? I wish I had tried locate before blowing that file away so I could see if locate could find those type of files for me. |
lc worked correctly before. it is the terminal handling of control chars that is problematic.
Quote:
Code:
% locate \* | grep ^V^M | lessthe above is "list all files from the locate database, look for occurrences of control-M, pipe to less so that they're highlighted." entering control-V escapes the next character to be a control char, in this case control-M |
eriklager,
Thanks, the modified script works well. pmccann, Problem solved. iTunes 3 added the Composer, Disc Number, Disc Count, Date Modified, Play Count, Last Played, and My Rating fields to the Library.export file now called Library.text. I just changed the numbers of the chunks of information in: my ($artist,$album,$song,$time,$track)=(split "\t",$_)[1,2,0,5,6]; and it works very well again. Thanks. Cheers... |
one more tweak...maybe?
I've gotten used to having my directories in color using the --color=always switch with ls (I have an alias for it), adding it to the script like this:
Code:
open LS,"ls -aF --color=always -- $ARGV[0]|"; # pipe the output of ls to this haEDIT: For that matter, it would be neat if the category headers matched the color of their contents. :) |
I detected a bug in the modified version: I had put a colon on line 8, after adfshrl. The colon makes the script expect an argument to the l option.
I'll edit my previos post to correct the problem. Erik ;) |
Re: one more tweak...maybe?
Quote:
|
btw, I followed this method I used to get color in my ls command:
http://www.resexcellence.com/terminal/05-09-01.shtml |
[pedantic] should close LS
Okay, it doesn't matter in this script since it will exit soon and so clean itself up anyway, but I feel that if you 'open' something, you ought to 'close' it when you are done. So I would add a statement:
close LS; after the line: my @what = <LS>; Sorry to be so pedantic, but I worry that someone might go writing a Perl script where they do an 'open' inside a loop and don't realize that they need to 'close'. |
***Post edited to reflect the fact that the required perl module is part of the default install. Apologies to anyone misled by my earlier claim that it had to be downloaded and installed separately.***
[[Re the "close" statement: I'm really at a loss to see what it adds, and how you'd get into trouble here by not adding explicit closes. Well, such behaviour would definitely cause some problems with more exotic pipes and networking scripts, but here it doesn't really matter. If you happen to reopen on the same filehandle the old handle is closed first, so you're covered there. Maybe I lack imagination... Maybe I'm just lazy... Maybe I expect things to "just work"... Ah well... It certainly never *harms* to have it there, so if symmetry's your friend then you'll probably want a close on the back of every open.]] The colour request. Hmm. Not quite as straightforward as I might have hoped, but not too bad. My final substantial posts on this thread attempt to throw a little colour into the mix; with the emphasis on "little", in that the colour specifications are very limited, but might just be enough to satisfy those of you hankering for blue or green or red or magenta or yellow in various thicknesses and styles! See the examples in the script below, and the full list of colours and styles by reading: % perldoc term::ansicolor Those of you who use transparency in the terminal will see exactly how the "on_white" colouring works. (Note that I've use "green on_white" for directory names, but "transparent" blue for file names, just to show the difference.) Those parts of the terminal window not drawn to will stay transparent, the pieces under the "on_white" output become solid. You can of course simply remove the "underlay", and your terminal will remain just as transparent as it ever was. The other slightly "interesting" aspect is that this sort of thing doesn't play very nicely withe paging programs like "less", which don't acknowledge the presence of the escape sequences correctly. Term::ANSIColor talks about generating the sequences at the start and end of each line: that doesn't work for the terminal in OS X (or for an xterm under XDarwin. Maybe the problem lies with "less"? Anyway, I've given up trying to chase what's happening, so here it is (in the next post), with that particular wart exposed. (Just use "page up" to see long listings!) For what it's worth, the standard "coloriser" that I use --obtained by simply adding "set color" to my .cshrc and then using ls-F (no space before the -F) to list in color-- also fails under the pipe to less, but in a slightly more elegant fashion. Cheers, Paul Oh yeah, thanks to Erik for his welcome additions |
The script with a bit of colour added
#!/usr/bin/perl -w
use strict; use Getopt::Std; $Term::ANSIColor::EACHLINE="\n"; use Term::ANSIColor; use vars qw($opt_a $opt_f $opt_h $opt_d $opt_s $opt_r $opt_l); my $screenwidth = 80; # adjust if necessary my $dir_color = "green on_white"; my $dir_header = "bold white on_green"; my $file_color = "blue "; my $file_header = "bold white on_blue"; my $dirs_topstring = "+----------------- Directories "; #adjust as desired! my $files_topstring = "+--------------------- Files "; getopts('adfshrl'); # These are the valid options: see help message if (!($opt_f || $opt_d || $opt_h)){ $opt_f=1; $opt_d=1 }; # show both if neither option present (and it's not a help request) warn <<HELP and exit if $opt_h or $ARGV[1]; usage: lc [-adfsrhl] [directory] A bare "lc" will separate files and directories, and sort them in a case insensitive manner down the screen (ie in columns). To sort case sensitively, or to display results in rows, or to show just files or just directories use the flags as outlined below. Flags may be grouped together (so "lc -ad" is the same as "lc -a -d") -------------------------------------------------------------------------------- -a : Include directory entries whose names begin with a dot (.) -d : Show directories -f : Show files (note that a bare "lc" will show both) -s : Sort case sensitively (eg "Lemon" before "apple") -r : List the output in rows instead of columns -h : Display this message -l : Display link (@) and exec (*) status on files displayed HELP $ARGV[0] or $ARGV[0] = "."; open LS,"ls -aF -- $ARGV[0]|"; # pipe the output of ls to this handle my @what = <LS>; # catch the output from the ls -F command $opt_a or @what = grep(!/^\./,@what); # filter out dot files? my @dirs = grep /\/$/,@what; # directories end with "/" my @files = grep /[^\/]\n$/,@what; # the rest (incl. links) if (@dirs && $opt_d){ # process dirs, if they exist and are requested my @sdirs = map {substr $_,0,-2} @dirs; # kill / and newline at once bannerprint($dirs_topstring); displaylist(\@sdirs,$dir_color); } if (@files && $opt_f){ # show files, if they exist and are requested chomp @files; $opt_l or s/\*|\@$// foreach (@files); # remove exec/link status? bannerprint($files_topstring); displaylist(\@files,$file_color); } ### Subroutines used above ### sub bannerprint{ my $starter = shift; my $topstring = $starter.("-"x($screenwidth-length($starter)-1))."+\n"; print colored $topstring,$dir_header if ($topstring=~/Direct/); print colored $topstring,$file_header if ($topstring=~/File/); print color 'reset' } sub displaylist{ my $array_ref = shift; my $color = shift; my $disp_len = getlen($array_ref)+1; my $num_disp_perline = int(80/${disp_len}); my @list = @{$array_ref}; @list = sort {lc($a) cmp lc($b)} @list unless $opt_s; if ($opt_r){ # for some perverted reason they want rows... OK!! my $j=0; my $out_stuff; foreach (@list){ $out_stuff.=sprintf "%-${disp_len}s", $_; $j++; $out_stuff.= "\n" if $j % ${num_disp_perline} == 0; } $out_stuff.= "\n"; print colored $out_stuff, $color; print color 'reset'; }else{ # here's one revolting way to print out columns my $length = @list; my $numrows = int($length/$num_disp_perline); $numrows++ if ($length/$num_disp_perline - $numrows); # extra row if doesn't divide evenly (ie there's a remainder) my $out_stuff; foreach my $i (1..$numrows){ my $j=0; my @row = grep {$j++;($i-$j)%$numrows == 0} @list; foreach (@row){ $out_stuff.=sprintf "%-${disp_len}s",$_; # print "Outstuff is now : ($j th row)\n$out_stuff\n"; } $out_stuff.="\n"; } print colored $out_stuff, $color; print color 'reset'; } } sub getlen{ my $arr_ref = shift; my $blen = 0; my $len = 0; foreach (@{$arr_ref}){ $len = length($_); $blen = $len if $len>$blen; } $blen++; # return one greater than the longest word } |
Paul,
I guess you missed my post about the bug i created :). If you remove the : in 'adfshrl:' on line 15 (in the script with color, it was line 8 in the original script) you will get the -l flag back working again. I put the colon there when I tested something and forgot to remove it. Erik |
pmccann,
I get the following: 'Can't find string terminator "HELP" anywhere before EOF at /Users/pm/bin/lc1 line 18'. Edited Got it to work, forgot about deleting the space mentioned by MervTormel. Works great! Thanks. Cheers... |
Hi Erik,
yep, don't know how that crept back into the script. I *did* read your earlier message, but must have started working from an earlier version of the script. I've edited the post above in order to insert the colon where all good colons belong... out of sight. Here's to crap colour then. (And to a frozen feature set :-) The thing's too long already!) Anyway, hope it gives a couple of people ideas for other scripts. Cheers, Paul |
werx great and is so purdy!
:) |
Very nice! :) I still had to remove the space after "HELP" on line 36, but no problem there. Thanks for adding the color. I like it. :)
|
| All times are GMT -5. The time now is 06:20 PM. |
Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2014, vBulletin Solutions, Inc.
Site design © IDG Consumer & SMB; individuals retain copyright of their postings
but consent to the possible use of their material in other areas of IDG Consumer & SMB.