How i can obtain differents data on a single pass?

#!/bin/bash

for i in `ls -c1 /usr/share/applications`
do
#name=`cat /usr/share/applications/$i | grep ^Name= | cut -d = -f2`
#categories=`cat /usr/share/applications/$i | grep ^Categories= | sed 's/;/=/g' | cut -d = -f2`
name=$(grep ^Name=  /usr/share/applications/$i | cut -d = -f2)
executable=$(grep ^Exec= /usr/share/applications/$i | cut -d = -f2)
icon=$(grep ^Icon= /usr/share/applications/$i | cut -d = -f2)
categories=$(grep ^Categories= /usr/share/applications/$i | sed 's/;/=/g' | cut -d = -f2)
echo $name
echo $icon
echo $executable
echo $categories
done

Actually i make 4 access on the single file for obtain each time one single data,is it possible obtain all the data on a single pass?
While is ok for me maintain 4 distinct variable.
Naturally using only bash or bash+sed or bash+awk or bash+grep+cut.

Can you provide a sample of the file?

Thanks i already resolved my initial questions few moment ago. :slight_smile:

#!/usr/bin/awk -f
BEGIN {
    print "Dynamic {"
    while ( ( "ls -c1 /usr/share/applications" | getline line ) >0 ) {
        #print line
        #print "/usr/share/applications/"line 
        while ( ( "cat /usr/share/applications/"line  | getline line1 ) >0 ) {
            #print line1
            if (match(line1,/^Name=/)) 
                name=substr(line1, 6, 30)
            if (match(line1,/^Exec=/)) 
                executable=substr(line1, 6, 30)
            if (match(line1,/^Icon=/)) 
                icon=substr(line1, 6, 30)
            if (match(line1,/^Categories=/)) { 
                categories=substr(line1, 12, 30)
                n=index(categories,";")
                categories=substr(categories, 1, n-1)
            }
        }
        #print name
        #print executable
        #print icon
        #print categories
        print  "Entry = \42" name  "\42 {Actions = \42Exec " executable "\42 }"
        # L'output deve venire con gli apici doppi attorno al nome ed all'eseguibile.
        # Entry = "Xarchiver" { Actions = "Exec xarchiver" }
        #c++        
    }
    exit
}
END  {
    print "}"
    #print "Total files: ", c
}

Problem solved.:smiley:

I'm impressed you managed a useless use of cat while constrained to the limitations of awk.

Why not just while(getline <filename) ?

I find myself doing this sort of thing a fair bit and setting up a local param_val() function makes the code much more maintainable/extendable/readable,
and as long as your only processing a few files the overhead can pretty much be ignored, more often than not the file will still be in the cache anyway.

Remember your going to be comming along in a year or two and going "What the $%#$ is this damn awk script doing!"

#!/bin/bash
 
function param_val {
    sed -n "s/^${1}=//p" $2 2> /dev/null
}
 
 
for i in `ls -c1 /usr/share/applications`
do
    CF=/usr/share/applications/$i
    name=$(param_val Name $CF)
    executable=$(param_val Exec $CF)
    icon=$(parm_val Icon $CF)
    categories=$(param_val Categories $CF | cut -d\; -f1)
done
1 Like

I don't see the need for the ls either:

#!/usr/bin/env ksh
awk '
    function printit( )
    {
        print  "Entry = \42" name  "\42 {Actions = \42Exec " executable "\42 }"
        name = executable = icon = categories = "";         # reset to prevent cary over if next file is missing data
    }

    BEGIN { print "Dynamic {" }

    NR > 1 && FILENAME != last_file { printit( ); }

    { last_file = FILENAME; }

    /^Name=/    { name=substr( $0, 6, 30); next; }
    /^Exec=/    { executable=substr( $0, 6, 30); next; }
    /^Icon=/        { icon = substr( $0, 6, 30 ); next; }
    /^Categories=/ { gsub( ";.*", "" ); categories=substr( $0, 12, 30); next; }

    END  {
        printit( );
        print "}"
    } ' /usr/share/applications/*

Setting a hard stop at 30 for each substr seems wrong, but without knowing exactly what the input data is it's hard to say what is better.

1 Like

I need line1, i tried to replace:

while ( ( "cat /usr/share/applications/"line  | getline line1 ) >0 ) {

with

while((getline line1 < "/usr/share/applications/"line )>0 ) {

but on this way i obtain a wrong output.

Entry = "" {Actions = "Exec " }

So how i can write it properly?

Interesting.
I fix one error:
icon=$(param_val Icon $CF)
and i put the output and now work. :slight_smile:
What mean the regular expression used on sed?

---------- Post updated at 10:14 AM ---------- Previous update was at 08:12 AM ----------

After more then one hour of search on that follow command:

sed -n "s/^${1}=//p" $2 2> /dev/null

i don't understand why use the symbol { and } over the first parameter.
And the last:
2.
While the /p if i understand correctly from Sed - An Introduction and Tutorial means:

I even make an attempt on test of the sed outside the script but i don't known where i made a mistake:

$ sed -e "s/^{Name}=//p" /usr/share/applications/firefox.desktop
Naturally using -n i don't have output so i used e.

You're looking for something too specific.

{ } work when substituting any shell variable in any context shell variables can be substituted in. It's normal syntax, and the safe way to do substitution, because it's less ambiguous. Would $var1_$var2 substitute $var1 or $var1_ ? The latter, to prevent that you'd do ${var1}_${var2}.

1 Like

I think that now i understand (at least about the { } ).
What about the final 2?
What means?

Yes -n causes nothing to print unless a /p is used this ensures only the line with your paramter is printed, to test from command try:

$ sed -n "s/^Name=//p" /usr/share/applications/firefox.desktop
1 Like

What means the last 2?
If i put 1 a receive an output with empty variable:

 Entry = "" { Actions = "Exec " }

while if i put 2 or more i receive the correct output.

---------- Post updated at 10:12 AM ---------- Previous update was at 07:33 AM ----------

During the test i find some particular .desktop.

That produce:

Entry = "DeaDBeeF
Play
Pause
Stop
Next
Prev" { Actions = "Exec deadbeef %F
deadbeef --play
deadbeef --pause
deadbeef --stop
deadbeef --next
deadbeef --prev" }

How is possible stop the search when the first occurrence is find on the file?

#!/bin/bash
 
function param_val {
    sed -n "s/^${1}=//p" $2 2> /dev/null
}
 
echo "Dynamic {" 
for i in `ls -c1 /usr/share/applications`
do
    CF=/usr/share/applications/$i
    name=$(param_val Name $CF)
    executable=$(param_val Exec $CF)
    icon=$(param_val Icon $CF)
    categories=$(param_val Categories $CF | cut -d\; -f1)
    echo " Entry = \"$name\" { Actions = \"Exec $executable\" }"
done
echo "}"

So only for the first block?

Here i read that using the q is possible quit before but i don't known how implement it.

$ sed -n "s/^Name=//p" /usr/share/applications/deadbeef.desktop

DeaDBeeF
Play
Pause
Stop
Next
Prev

This function should be closer to what you want - It stops at the first occurance.

function param_val {
    awk "/^${1}=/{gsub(/^${1}="'/,""); print; exit}' $2
}

eg:

$ awk '/^Name=/{gsub(/^Name=/,""); print; exit}' /usr/share/applications/deadbeef.desktop
DeaDBeeF
1 Like
#!/bin/bash

function param_val {
    awk "/^${1}=/{gsub(/^${1}="'/,""); print; exit}' $2
}
 
echo "Dynamic {" 
for CF in `ls -c1 /usr/share/applications/*.desktop`
do
    name=$(param_val Name $CF)

    executable=$(param_val Exec $CF)

    icon=$(param_val Icon $CF)

    categories=$(param_val Categories $CF | awk -F ";" '{print $1}')

    echo " Entry = \"$name\" { Actions = \"Exec $executable\" }"

done
echo "}"

I replace:
categories=$(param_val Categories $CF | cut -d\; -f1)
with:
categories=$(param_val Categories $CF | awk -F ";" '{print $1}')
for remove the dependency with the command line cut.

This is the first release with some minor adjustment.

Thanks for the help. :slight_smile: