in reply to Re: very new to perl; suggestions for porting this shell script to perl?
in thread very new to perl; suggestions for porting this shell script to perl?

That first suggestion is very helpful. 2 completely irrelevant files were being searched.

If you're more experienced with windows, those desktop files are basically like a more detailed version of a windows shortcut file. They're installed to /usr/share/applications upon installing most linux gui apps.

Here are the contents of gimp.desktop.
[Desktop Entry] Version=1.0 Type=Application Name=GNU Image Manipulation Program Name[ar]=&#1576;&#1585;&#1606;&#1575;&#1605;&#1580; &#1580;&#1606;&#16 +08; &#1604;&#1605;&#1593;&#1575;&#1604;&#1580;&#1577; &#1575;&#1604;& +#1589;&#1608;&#1585; Name[ast]=Programa de manipulación d'Imaxe GNU Name[be]=GNU Image Manipulation Program Name[bg]=&#1056;&#1077;&#1076;&#1072;&#1082;&#1090;&#1086;&#1088; &#10 +85;&#1072; &#1080;&#1079;&#1086;&#1073;&#1088;&#1072;&#1078;&#1077;&# +1085;&#1080;&#1103; (GIMP) Name[ca]=Programa de retoc d'imatges de GNU Name[ca@valencia]=Programa de retoc d'imatges de GNU Name[cs]=GNU Image Manipulation Program Name[da]=GNU Image Manipulation Program Name[de]=GNU Image Manipulation Program GenericName=Image Editor GenericName[ar]=&#1605;&#1581;&#1585;&#1585; &#1589;&#1608;&#1585; GenericName[ast]=Editor d'imaxe GenericName[be]=&#1056;&#1101;&#1076;&#1072;&#1082;&#1090;&#1072;&#108 +8; &#1074;&#1110;&#1076;&#1072;&#1088;&#1099;&#1089;&#1072;&#1118; GenericName[bg]=&#1056;&#1077;&#1076;&#1072;&#1082;&#1090;&#1086;&#108 +8; &#1085;&#1072; &#1080;&#1079;&#1086;&#1073;&#1088;&#1072;&#1078;&# +1077;&#1085;&#1080;&#1103; GenericName[ca]=Editor d'imatges GenericName[ca@valencia]=Editor d'imatges GenericName[cs]=Editor obrázk&#367; GenericName[da]=Billedredigering GenericName[de]=Bildeditor Comment=Create images and edit photographs Comment[ar]=&#1571;&#1606;&#1588;&#1574; &#1589;&#1608;&#1585;&#1575; +&#1608;&#1581;&#1585;&#1585; &#1604;&#1602;&#1591;&#1575;&#1578; Comment[ast]=Cree imáxenes y edite semeyes Comment[be]=&#1057;&#1090;&#1074;&#1072;&#1088;&#1101;&#1085;&#1100;&# +1085;&#1077; &#1074;&#1110;&#1076;&#1072;&#1088;&#1099;&#1089;&#1072; +&#1118; &#1110; &#1088;&#1101;&#1076;&#1072;&#1075;&#1072;&#1074;&#10 +72;&#1085;&#1100;&#1085;&#1077; &#1092;&#1072;&#1090;&#1072;&#1075;&# +1088;&#1072;&#1092;&#1110;&#1081; Comment[bg]=&#1057;&#1098;&#1079;&#1076;&#1072;&#1074;&#1072;&#1085;&# +1077; &#1085;&#1072; &#1080;&#1079;&#1086;&#1073;&#1088;&#1072;&#1078 +;&#1077;&#1085;&#1080;&#1103; &#1080; &#1088;&#1077;&#1076;&#1072;&#1 +082;&#1094;&#1080;&#1103; &#1085;&#1072; &#1089;&#1085;&#1080;&#1084; +&#1082;&#1080; Comment[ca]=Creeu imatges i editeu fotografies Comment[ca@valencia]=Creeu imatges i editeu fotografies Comment[cs]=Vytvá&#345;et obrázky a upravovat fotografie Comment[da]=Opret billeder og redigér fotografier Comment[de]=Bilder erstellen und Fotografien bearbeiten Exec=gimp-2.6 %U TryExec=gimp-2.6 Icon=gimp Terminal=false Categories=Graphics;2DGraphics;RasterGraphics;GTK;</b> X-GNOME-Bugzilla-Bugzilla=GNOME X-GNOME-Bugzilla-Product=GIMP X-GNOME-Bugzilla-Component=General X-GNOME-Bugzilla-Version=2.6.11 X-GNOME-Bugzilla-OtherBinaries=gimp-2.6 StartupNotify=true MimeType=application/postscript;application/pdf;image/bmp;image/g3fax; +image/gif;image/x-fits;image/pcx;image/x-portable-anymap;image/x-port +able-bitmap;image/x-portable-graymap;image/x-portable-pixmap;image/x- +psd;image/x-sgi;image/x-tga;image/x-xbitmap;image/x-xwindowdump;image +/x-xcf;image/x-compressed-xcf;image/tiff;image/jpeg;image/x-psp;image +/png;image/x-icon;image/x-xpixmap;image/svg+xml;image/x-wmf;



audacity.desktop:
[Desktop Entry] Name=Audacity Name[de]=Audacity Name[ru]=Audacity GenericName=Sound Editor GenericName[de]=Audio-Editor GenericName[ru]=&#1056;&#1077;&#1076;&#1072;&#1082;&#1090;&#1086;&#108 +8; &#1079;&#1074;&#1091;&#1082;&#1086;&#1074;&#1099;&#1093; &#1092;&# +1072;&#1081;&#1083;&#1086;&#1074; Comment=Record and edit audio files Comment[de]=Audio-Dateien aufnehmen und bearbeiten Comment[ru]=&#1047;&#1072;&#1087;&#1080;&#1089;&#1100; &#1080; &#1088; +&#1077;&#1076;&#1072;&#1082;&#1090;&#1080;&#1088;&#1086;&#1074;&#1072 +;&#1085;&#1080;&#1077; &#1079;&#1074;&#1091;&#1082;&#1086;&#1074;&#10 +99;&#1093; &#1092;&#1072;&#1081;&#1083;&#1086;&#1074; Icon=audacity Type=Application Categories=AudioVideo;Audio;AudioVideoEditing; Exec=audacity StartupNotify=false Terminal=false MimeType=application/ogg;audio/basic;audio/mpeg;audio/x-aiff;audio/x-m +p3;audio/x-wav;application/x-audacity-project;



Here is what's generated by the script from my .desktop files:
Submenu = "Audio" { Entry = "Ardour" { Actions = "Exec ardour3" } Entry = "Audacity" { Actions = "Exec audacity" } Entry = "Calf Plugin Pack for JACK" { Actions = "Exec calfjack +host" } Entry = "DeaDBeeF" { Actions = "Exec deadbeef" } Entry = "gtkpod iPod Manager" { Actions = "Exec gtkpod" } Entry = "JACK Rack" { Actions = "Exec jack-rack" } Entry = "JAMin" { Actions = "Exec jamin" } Entry = "Patchage" { Actions = "Exec /usr/bin/patchage" } Entry = "QjackCtl" { Actions = "Exec /usr/bin/qjackctl" } Entry = "Rakarrack" { Actions = "Exec rakarrack" } } Submenu = "Graphics" { Entry = "gbdfed" { Actions = "Exec gbdfed" } Entry = "GNU Image Manipulation Program" { Actions = "Exec gim +p-2.6" } Entry = "Inkscape" { Actions = "Exec inkscape" } Entry = "mtPaint" { Actions = "Exec mtpaint" } Entry = "Viewnior" { Actions = "Exec viewnior" } } Submenu = "Network" { Entry = "Mobloquer" { Actions = "Exec /usr/bin/mobloquer" } Entry = "Avahi SSH Server Browser" { Actions = "Exec /usr/bin/ +bssh" } Entry = "Avahi VNC Server Browser" { Actions = "Exec /usr/bin/ +bvnc" } Entry = "Firefox" { Actions = "Exec firefox" } Entry = "IcedTea Web Start" { Actions = "Exec /usr/lib/jvm/jav +a-6-openjdk/bin/javaws" } Entry = "Midori" { Actions = "Exec midori" } Entry = "Net Activity Viewer" { Actions = "Exec netactview" } Entry = "Nicotine-Plus" { Actions = "Exec nicotine.py" } Entry = "Transmission" { Actions = "Exec transmission-gtk" } } Submenu = "Settings" { Entry = "ARandR" { Actions = "Exec arandr" } Entry = "IcedTea Web Control Panel" { Actions = "Exec /usr/lib +/jvm/java-6-openjdk/bin/itweb-settings" } Entry = "Customize Look and Feel" { Actions = "Exec lxappearan +ce" } Entry = "Disk Utility" { Actions = "Exec palimpsest" } Entry = "Qt Config " { Actions = "Exec /usr/bin/qtconfig" } Entry = "TintWizard" { Actions = "Exec tintwizard" } } Submenu = "System" { Entry = "Avahi Zeroconf Browser" { Actions = "Exec /usr/bin/av +ahi-discover" } Entry = "BleachBit (Admin)" { Actions = "Exec bleachbit-admin" + } Entry = "BleachBit" { Actions = "Exec bleachbit" } Entry = "FSlint" { Actions = "Exec fslint-gui" } Entry = "GConf Cleaner" { Actions = "Exec gconf-cleaner" } Entry = "GParted" { Actions = "Exec gksu /usr/sbin/gparted" } Entry = "gtk-iptables" { Actions = "Exec /usr/bin/gksu gtkipta +bles" } Entry = "Htop" { Actions = "Exec htop" } Entry = "Task Manager" { Actions = "Exec lxtask" } Entry = "PCManFM-Mod File Manager" { Actions = "Exec pcmanfm-m +od" } Entry = "rxvt-unicode" { Actions = "Exec urxvt" } } Submenu = "Utility" { Entry = "ChmSee" { Actions = "Exec chmsee" } Entry = "Archive Manager" { Actions = "Exec file-roller" } Entry = "Figaro's Password Manager 2" { Actions = "Exec fpm2" +} Entry = "Redshift" { Actions = "Exec gtk-redshift" } Entry = "LilyTerm" { Actions = "Exec lilyterm" } Entry = "Disk Utility" { Actions = "Exec palimpsest" } Entry = "Search for Files or Folders" { Actions = "Exec pcmanf +m-mod --find-files" } Entry = "PCManFM-Mod File Manager" { Actions = "Exec pcmanfm-m +od" } }


With certain applications I had previously installed, the desktop files would occasionally contain one or more sections in addition to the [Desktop Entry] section , which would often contain another Name=xxxx and Exec=xxxx line. This is why I would like to find a way to either limit the regex match to only the first instance or else be able to specify a start/stop range for the matching.

I appreciate your willingness to help despite not having much to go by. Thanks alot.

Replies are listed 'Best First'.
Re^3: very new to perl; suggestions for porting this shell script to perl?
by Cristoforo (Curate) on May 29, 2011 at 20:35 UTC
    Yes, the sample files and output clarified the problem.

    Here is a solution, but you should probably check it to see if it meets your specifications.

    #!/usr/bin/perl use strict; use warnings; my @files = glob "/usr/share/applications/*.desktop"; my $regex = join "|", qw/ Audio Graphics Network Settings System Utili +ty /; my %data; FILE: for my $file (@files) { open my $fh, "<", $file or die "Unable to open $file (for reading) +. $!"; my ($category, $name, $exec); while (<$fh>) { if(/^Categories=.*?\b($regex)\b/) { $category ||= $1; # sets cat for first time only (each fil +e) } elsif (/^Name=(.+)$/) { $name ||= $1; # sets name first time only (each file) } elsif (/^Exec=(\S+)/) { $exec ||= $1; # sets exec first time only (each file) } } close $fh or die "Unable to close $file. $!"; for ($category, $name, $exec) { if (! defined) { warn "$file missing category or name or exec.\n"; # skip the 'push' below and go to the label 'FILE:' next FILE; } } push @{ $data{ $category } }, {name => $name, exec => $exec}; } for my $category (keys %data) { print "Submenu = \"$category\" {\n"; my @recs = @{ $data{ $category } }; for my $href ( @recs ) { # hash reference print " Entry = \"$href->{name}\" { Actions = \"$href->{ex +ec}\" }\n"; } print "}\n"; }

    Notice how all the variables are declared with my. Declaring variables this way helps catch spelling errors and also limits the scope to the innermost containing block. This property is handy and can be seen in the declaration of my ($category, $name, $exec)

    Every time the for loop starts a new iteration, (with a new file), it gets a fresh copy of 'undefined' variables. Then, later on they are assigned to, ($category ||= $1;). The first time a valid match is made, an assignment will happen. If there are more than 1 instance of name or exec (as you noted in your post), they will be ignored. This is because any of the three already has the first match assigned to it.

    The data is stored in the %data hash (which is then printed out when the loop terminates).

    The glob function does wildcard expansion and returns all the matching files. (gimp.desktop and all other files with the .desktop extension).

    There are many other Perl idioms here and if you have questions about just why a certain piece is written the way it is, just ask.

    If you plan on using Perl now on, it would be probably helpful to read a book such as Learning Perl witch is a good intro to Perl or Programming Perl which is larger but a good reference book.

      It's working great! I've only made a few changes. I changed the (/^Exec=(\S+)/) back to (/^Exec=(.*)/) because I still want to preserve the flags present at the end of some "Exec=xxx" lines (for example: "Exec=pcmanfm-mod --find-files" which corresponds to a "Name=Search for Files and Folders"), while still excluding %f, %F, %u and %U from any entries, because these act as placeholders for the filenames or urls on which the user has right-clicked.

      The new script did create a small issue, however. I noticed that it prints the submenus in a different order from how they're listed. Do you know of any simple ways to make it respect the order in which they're specified when they're assigned at "my $regex ="? If not, it's completely usable how it is and I'll keep it in mind as I'm reading some of the books you suggested.

        Oops.. That last comment was mine. I had forgotten to login.
        I'm glad to hear it works OK. :-)
        You never really know until you run it against some data I guess.

        Do you know of any simple ways to make it respect the order in which they're specified when they're assigned at "my $regex ="?
        That occured to me after I posted the code, but I wasn't sure it was necessary.

        Since your categories list is in alphabetical order, you could sort the keys in the print routine like:

        for my $category (sort keys %data).

        Glad to be of help!

        Update: Just for the record, if the order of records was not in sort order, a few small changes would handle that situation.

        At the top of the script, before the for loop, you would enter:

        my @cat = qw/ Audio Graphics Network Settings System Utility /; # in y +our desired order my $regex = join "|", @cat; my @files = glob "/usr/share/applications/*.desktop"; # as before

        Then a small change to the print routine:

        for my $category ( @cat ) { next unless exists $data{ $category }; print "Submenu = \"$category\" {\n"; . . . . .