perl-diddler has asked for the wisdom of the Perl Monks concerning the following question:

Forgive me if I'm committing a faux pas, but it appears my previous attempt at posting this problem was marred by my confusing description and the mangling of my input by the website-input-filtering mechanisms. So I'm trying "anew" to rewrite an earlier post (http://perlmonks.org/?node_id=659165)that was unclear about the problem description, what I was trying to do, and was further "unclarified" by text characters being stripped out (Why are square brackets "reserved" characters that need to be listed in using the "ampersand-pound-number" syntax? Sigh!)

My problem isn't a misunderstanding of where shell expansion occurs (by definition I'm attempting to refer to expansion that occurs in my shell (bash, with extglob set) -- not in perl).

Sidenote: I didn't know that the "<code>" tag acted differently within a paragraph (as a literalizing tag) vs. outside of a paragraph.

I want to pass a shell expansion sequence to bash and have it expanded. I keep running into problems concerning where things are expanded and how to quote things so they don't get dequoted -- part of my problem may be my attempt to use backquotes.

I started using backquotes with simple shell expansion characters -- and that worked for simple expansions.

My program is assuming it is running in (or one level above) my "directory of interest). The directory of interest comes from a SuSE10.3 distribution. The directory I'm interested in is an architecture-specific directory (x86_64). In this directory I have RPM's of the form: <productname>-<version>-<release>.<arch>.rpm, were version and release do not contain "-" (a dash). Some of the products have a 32bit version as well as a 64bit version. The 32bit versions have the same form as the 64bit versions but with the string "-32bit" added after the product name. Thus, for <productname>-<version>-<release>.<arch>.rpm, there may also be an rpm <productname>-32bit-<version>-<release>.<arch>.rpm. NOTE: for purposes of this older scriptlet, I didn't need to separate out the release from the version, so the 'version' string/variable contains: $v=$v."-"."$r";. To detect those two variants, I wrote a sub "expand_path":

sub expand_path ($$$\$) { # inputs: (product,version,is_32bit(T/F), ptr(expanded_path)) # output: (-,-,-, expanded_path) # returns T/F (successful path expansion) my ($p, $v, $is32bitbit, $expp) = @_; my $is32_text = $is32bitbit ? "32bit-" : ""; my $rpm = ($v !~ /\.rpm$/) ? "$p-$is32_text$v-*.rpm" : "$p-$is32_text$v*"; my $exp; if (defined ($exp = `2>&1 bash -c "echo $rpm"`)) { chomp $exp; if ($exp !~ /\*/) { $$expp=$exp; return 1; } } $$expp = $rpm; return 0; }
This worked for an earlier script -- I called it once with the $is_32bit set to false, and once set to true, to determine if I had 64 and 32bit versions of the packages present.

That's a 'working' scriptlet for a previous need. Now I'm in a different situation where I only have the product name (not the version or release) (derived from looking at what products are currently installed ("rpm -qa"). I want to find any products of the same name in my package directory (and, eventually, also look for 32bit versions the same as I did in my previous script, above.

My problem is I'm having trouble using bash's extended regular expression characters (enabled via "shopt -o extglob") calling them in a similar way. For reference, the "interesting", extended globbing patterns in bash (from the manpage are:

If the *extglob* shell option is enabled using the *shopt* builtin, se +veral extended pattern matching operators are recognized. In the followin +g description, a pattern-list is a list of one or more patterns separate +d by a |. Composite patterns may be formed using one or more of the fol +- lowing sub-patterns: ?(pattern-list) Matches zero or one occurrence of the given patterns *(pattern-list) Matches zero or more occurrences of the given patterns +(pattern-list) Matches one or more occurrences of the given patterns @(pattern-list) Matches one of the given patterns !(pattern-list) Matches anything except one of the given patterns
The rpm pattern I am using, to search through the "gcc" packages on disk (ignoring the 32bit packages for now) is:
echo gcc-+([^-])-+([^-]).rpm
In bash, this correctly selects the 1 matching package:
gcc-4.2-24.x86_64.rpm
. Note: The input rpm list of (43) "gcc-*.rpm" packages is:
gcc-32bit-4.2-24.x86_64.rpm gcc-4.2-24.x86_64.rpm gcc-ada-4.2-24.x86_64.rpm gcc-c++-4.2-24.x86_64.rpm gcc-fortran-32bit-4.2-24.x86_64.rpm gcc-fortran-4.2-24.x86_64.rpm gcc-gij-32bit-4.2-24.x86_64.rpm gcc-gij-4.2-24.x86_64.rpm gcc-info-4.2-24.x86_64.rpm gcc-java-4.2-24.x86_64.rpm gcc-locale-4.2-24.x86_64.rpm gcc-obj-c++-4.2-24.x86_64.rpm gcc-objc-32bit-4.2-24.x86_64.rpm gcc-objc-4.2-24.x86_64.rpm gcc41-32bit-4.1.3_20070724-15.x86_64.rpm gcc41-4.1.3_20070724-15.x86_64.rpm gcc41-ada-4.1.3_20070724-15.x86_64.rpm gcc41-c++-4.1.3_20070724-15.x86_64.rpm gcc41-fortran-32bit-4.1.3_20070724-15.x86_64.rpm gcc41-fortran-4.1.3_20070724-15.x86_64.rpm gcc41-gij-32bit-4.1.3_20070724-25.x86_64.rpm gcc41-gij-4.1.3_20070724-25.x86_64.rpm gcc41-java-4.1.3_20070724-15.x86_64.rpm gcc41-locale-4.1.3_20070724-15.x86_64.rpm gcc41-obj-c++-4.1.3_20070724-15.x86_64.rpm gcc41-objc-32bit-4.1.3_20070724-15.x86_64.rpm gcc41-objc-4.1.3_20070724-15.x86_64.rpm gcc41-testresults-4.1.3_20070724-25.x86_64.rpm gcc42-32bit-4.2.1_20070724-17.x86_64.rpm gcc42-4.2.1_20070724-17.x86_64.rpm gcc42-ada-4.2.1_20070724-17.x86_64.rpm gcc42-c++-4.2.1_20070724-17.x86_64.rpm gcc42-fortran-32bit-4.2.1_20070724-17.x86_64.rpm gcc42-fortran-4.2.1_20070724-17.x86_64.rpm gcc42-gij-32bit-4.2.1_20070724-26.x86_64.rpm gcc42-gij-4.2.1_20070724-26.x86_64.rpm gcc42-info-4.2.1_20070724-17.x86_64.rpm gcc42-java-4.2.1_20070724-17.x86_64.rpm gcc42-locale-4.2.1_20070724-17.x86_64.rpm gcc42-obj-c++-4.2.1_20070724-17.x86_64.rpm gcc42-objc-32bit-4.2.1_20070724-17.x86_64.rpm gcc42-objc-4.2.1_20070724-17.x86_64.rpm gcc42-testresults-4.2.1_20070724-26.x86_64.rpm

Someone suggested (before I edited the older post and protected the shell pattern from input filtering in the post) that I try the pattern [equiv] echo gcc-[^-]*-[^-]*.rpm. While this narrows down the list and works when used in perl's "system()" call, it doesn't provide the single rpm I wanted. It's output (14 entries) was/is:

gcc-32bit-4.2-24.x86_64.rpm gcc-4.2-24.x86_64.rpm gcc-ada-4.2-24.x86_64.rpm gcc-c++-4.2-24.x86_64.rpm gcc-fortran-32bit-4.2-24.x86_64.rpm gcc-fortran-4.2-24.x86_64.rpm gcc-gij-32bit-4.2-24.x86_64.rpm gcc-gij-4.2-24.x86_64.rpm gcc-info-4.2-24.x86_64.rpm gcc-java-4.2-24.x86_64.rpm gcc-locale-4.2-24.x86_64.rpm gcc-obj-c++-4.2-24.x86_64.rpm gcc-objc-32bit-4.2-24.x86_64.rpm gcc-objc-4.2-24.x86_64.rpm

------------------------------------------------------

Unclearly, I wrote in my earlier post "Double quotes, single, adding bash -c 'echo...' in the backquotes, extra backslashes in front of the problematic parens. Part of the prob was I needed to set the extglob option (shopt -s extglob; echo <pattern>).

But every type of backslashed sequence either doesn't expand, or comes up completely empty, or errors pop out. "

What I meant was that I had tried several variations, at first from the command line (using perl -e 'script') and then using a perl-scriptlet to eliminate quoting side-effects from my interactive shell. The test-scriptlet has used variations on this:

#!/usr/bin/perl $pat='+([^-])-+([^-]).rpm'; $a=`bash -c "shopt -s extglob ;echo gcc-$pat"`; print "a=$a\n";
But the above gives output:
bash: -c: line 0: syntax error near unexpected token `(' bash: -c: line 0: `shopt -s extglob ;echo gcc-+([^-])-+([^-]).rpm' a=
Other variations on the above also fail:
$pat='+\([^-]\)-+\([^-]\).rpm';
gives output:
a=gcc-+([^-])-+([^-]).rpm
That seems to inhibit path expansion (yet without it, the parens generate an error).

I've tried other "random" variations, some that I wouldn't think would work (but when all else fails, that which has not been tried may...(but not yet...)

(double backslash in front of parens:) #!/usr/bin/perl $pat='+\\([^-]\\)-+\\([^-]\\).rpm'; $a=`bash -c 'shopt -s extglob ;echo gcc-$pat'`; print "a=$a\n";
output:
a=gcc-+([^-])-+([^-]).rpm

More backslashes (triple & quad) are no better.
At this point, I'm at a loss to explain why I am unable to get the pattern to work safely in backslashes.

Any ideas? (!I hate "quote" problems!!)
L
(p.s. - hopefully I've explained things in enough details, clearly enough and the intended examples. *sigh* )

Replies are listed 'Best First'.
Re: quoting problems passing pattern to shell in backquotes
by almut (Canon) on Dec 28, 2007 at 05:33 UTC
    bash: -c: line 0: syntax error near unexpected token `('

    As I noted in my reply to your other node, you have to use \n instead of the semicolon to separate the shopt -s extglob statement from the rest, i.e.

    #!/usr/bin/perl $pat='+([^-])-+([^-]).rpm'; $a=`bash -c "shopt -s extglob\necho gcc-$pat"`; print "a=$a\n";
Re: quoting problems passing pattern to shell in backquotes
by polettix (Vicar) on Dec 28, 2007 at 10:00 UTC
    While I understand playing on one's strenghts, I think you're using the wrong tool for the job. Perl is quite good at playing with text, so I don't fully understand your insistence upon using shell expansions. Apart from lack of knowledge of better tools, maybe :)

    I'd suggest you to take a look to File::Find::Rule. Your problem can be solved quite easily:

    #!/usr/bin/env perl use strict; use warnings; use File::Find::Rule; my $pattern = qr/ \A gcc # start with "gcc" - # first separating hyphen [^-]+ # first sequence of non-hyphen - # second - also last - separating hyphen [^-]+ # second sequence of non-hypen \.rpm \z # end with ".rpm" /mxs; my @files = File::Find::Rule->file()->name($pattern)->maxdepth(1)->in( +'.'); print "files: [@files]\n";
    No shell expansion quoting headaches, no process spawning, comments in your pattern... and it even works!
    poletti@PolettiX:~/sviluppo/perl/testdir$ ls findit.pl gcc-32bit-4.2-24.x86_64.rpm gcc41-32bit-4.1.3_20070724-15.x86_64.rpm gcc41-4.1.3_20070724-15.x86_64.rpm gcc41-ada-4.1.3_20070724-15.x86_64.rpm gcc41-c++-4.1.3_20070724-15.x86_64.rpm gcc41-fortran-32bit-4.1.3_20070724-15.x86_64.rpm gcc41-fortran-4.1.3_20070724-15.x86_64.rpm gcc41-gij-32bit-4.1.3_20070724-25.x86_64.rpm gcc41-gij-4.1.3_20070724-25.x86_64.rpm gcc41-java-4.1.3_20070724-15.x86_64.rpm gcc41-locale-4.1.3_20070724-15.x86_64.rpm gcc41-objc-32bit-4.1.3_20070724-15.x86_64.rpm gcc41-obj-c++-4.1.3_20070724-15.x86_64.rpm gcc41-objc-4.1.3_20070724-15.x86_64.rpm gcc41-testresults-4.1.3_20070724-25.x86_64.rpm gcc-4.2-24.x86_64.rpm gcc42-32bit-4.2.1_20070724-17.x86_64.rpm gcc42-4.2.1_20070724-17.x86_64.rpm gcc42-ada-4.2.1_20070724-17.x86_64.rpm gcc42-c++-4.2.1_20070724-17.x86_64.rpm gcc42-fortran-32bit-4.2.1_20070724-17.x86_64.rpm gcc42-fortran-4.2.1_20070724-17.x86_64.rpm gcc42-gij-32bit-4.2.1_20070724-26.x86_64.rpm gcc42-gij-4.2.1_20070724-26.x86_64.rpm gcc42-info-4.2.1_20070724-17.x86_64.rpm gcc42-java-4.2.1_20070724-17.x86_64.rpm gcc42-locale-4.2.1_20070724-17.x86_64.rpm gcc42-objc-32bit-4.2.1_20070724-17.x86_64.rpm gcc42-obj-c++-4.2.1_20070724-17.x86_64.rpm gcc42-objc-4.2.1_20070724-17.x86_64.rpm gcc42-testresults-4.2.1_20070724-26.x86_64.rpm gcc-ada-4.2-24.x86_64.rpm gcc-c++-4.2-24.x86_64.rpm gcc-fortran-32bit-4.2-24.x86_64.rpm gcc-fortran-4.2-24.x86_64.rpm gcc-gij-32bit-4.2-24.x86_64.rpm gcc-gij-4.2-24.x86_64.rpm gcc-info-4.2-24.x86_64.rpm gcc-java-4.2-24.x86_64.rpm gcc-locale-4.2-24.x86_64.rpm gcc-objc-32bit-4.2-24.x86_64.rpm gcc-obj-c++-4.2-24.x86_64.rpm gcc-objc-4.2-24.x86_64.rpm poletti@PolettiX:~/sviluppo/perl/testdir$ perl findit.pl files: [gcc-4.2-24.x86_64.rpm]

    Hey! Up to Dec 16, 2007 I was named frodo72, take note of the change! Flavio
    perl -ple'$_=reverse' <<<ti.xittelop@oivalf

    Io ho capito... ma tu che hai detto?