LinuxQuestions.org
Latest LQ Deal: Latest LQ Deals
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - General
User Name
Password
Linux - General This Linux forum is for general Linux questions and discussion.
If it is Linux Related and doesn't seem to fit in any other forum then this is the place.

Notices


Reply
  Search this Thread
Old 02-20-2012, 07:17 PM   #1
romagnolo
Member
 
Registered: Jul 2009
Location: Montaletto
Distribution: Debian GNU/Linux
Posts: 107

Rep: Reputation: 5
Bash: extended regex pattern 'NOT' disabled inside parameter expansion?


I wouldn't wish to keep up my bash saga here. But web seems empty on this.
I just found that some of the extended features of pathname expansions (those that get activated by the shell built-in shopt -s extglob) don't work when the regex pattern is integrated into parameter expansions, but they do work singularly on paths, as they are meanly meant for.
Namely, I tested for the 'NOT' operator !(). On path names, I experience correct behaviours:
Code:
$ shopt -s extglob
$ ls
full.jpg  full.jpg.meta  half.jpg  half.jpg.meta
$ ls !(*meta)
full.jpg  half.jpg
but the equivalent job bounding the regex with a parameter expansion, the pattern substitution, does not work:
Code:
$ shopt -s extglob
$ ls
full.jpg  full.jpg.meta  half.jpg  half.jpg.meta
$ x=$(ls)
$ echo _${x//!(*meta)/}_
__
I would expect the regex pattern to expand like it does in the first box, match everything not containing *meta, substitute the matches with an empty string, and feed 'echo' with the 2 remaining filenames 'full.jpg' and 'half.jpg'.
Also, I think this time The Manual is by my side
Quote:
${parameter/pattern/string}
Pattern substitution. The pattern is expanded to produce a pattern just as in pathname expansion. Parameter is expanded and the longest match of pattern against its value is replaced with string. If pattern begins with /, all matches of pattern are replaced with string. Normally only the first match is replaced. [...] If string is null, matches of pattern are deleted and the / following pattern may be omitted.

Last edited by romagnolo; 02-20-2012 at 07:20 PM.
 
Old 02-20-2012, 10:37 PM   #2
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
Blog Entries: 31

Rep: Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208
The difference is caused by the pattern matching in ls !(*meta) operating on each file individually whereas ${x//!(*meta)/} operates on the whole string.

The first match for !(*meta) is "full.jpg full.jpg.meta half.jpg half.jpg.met". This is removed leaving "a" which also matches !(*meta) so is removed as the second match:
Code:
c@CW8:/tmp/try$ echo _${x/!(*meta)/}_
echo _a_
Incidentally, x=$(ls) does not put the same value in x as is shown by ls run at the command prompt. ls adjusts its ouptput formatting according to what it is writing to. The -x option can be used to workaround this behaviour:
Code:
c@CW8:/tmp/try$ ls
full.jpg  full.jpg.meta  half.jpg  half.jpg.meta
c@CW8:/tmp/try$ x=$(ls)
c@CW8:/tmp/try$ echo "$x"
full.jpg
full.jpg.meta
half.jpg
half.jpg.meta
c@CW8:/tmp/try$ x=$(ls -x)
c@CW8:/tmp/try$ echo "$x"
full.jpg  full.jpg.meta  half.jpg  half.jpg.meta
A solution to your requirement is to pattern match space-separated words in the ls output:
Code:
c@CW8:/tmp/try$ x=$(ls -x)
c@CW8:/tmp/try$ echo _${x//*( )*([^ ])meta/}_
_full.jpg half.jpg_
That will fail when file names include space(s) so it's more robust to parse ls output which has one file name per line:
Code:
c@CW8:/tmp/try$ x=$(/bin/ls -1)
c@CW8:/tmp/try$ echo _${x//$'\n'*([^$'\n'])meta/}_
_full.jpg half.jpg_
But parsing ls output can never be fully robust for reasons explained here.

Last edited by catkin; 02-20-2012 at 10:39 PM. Reason: prettification
 
1 members found this post helpful.
Old 02-21-2012, 09:23 AM   #3
romagnolo
Member
 
Registered: Jul 2009
Location: Montaletto
Distribution: Debian GNU/Linux
Posts: 107

Original Poster
Rep: Reputation: 5
Quote:
Originally Posted by catkin View Post
The difference is caused by the pattern matching in ls !(*meta) operating on each file individually whereas ${x//!(*meta)/} operates on the whole string.
This is a valuable information the manual avoids to specify. I guess you learnt this by experience, or there is some special secret webpage somewhere?

Quote:
The first match for !(*meta) is "full.jpg full.jpg.meta half.jpg half.jpg.met". This is removed leaving "a" which also matches !(*meta) so is removed as the second match:
Code:
c@CW8:/tmp/try$ echo _${x/!(*meta)/}_
echo _a_
Incidentally, x=$(ls) does not put the same value in x as is shown by ls run at the command prompt. ls adjusts its ouptput formatting according to what it is writing to. The -x option can be used to workaround this behaviour: [...]
Thank you. Actually I noticed that spare "a" when using ${x/ instead of ${x//, but still I can't figure out how the pattern gets laid upon the match. Using the single-line output provided by the better ls -x, in my theory matching should be:
  • *meta : match the sub-string composed of any character terminated by "meta" on the right.
    So, although diverging from the original goal, it should match the whole string: "full.jpg full.jpg.meta half.jpg half.jpg.meta"
  • !(*meta) : it should invert the previous match of *meta, giving an empty string.
But in practice, the pattern generates two matches (why?): "full.jpg full.jpg.meta half.jpg half.jpg.met" and "a".
 
Old 02-21-2012, 10:00 PM   #4
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
Blog Entries: 31

Rep: Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208
No secret web page -- just curiosity and experimentation, seasoned with a dash of experience.

Rather than your concept of "match *meta and discard it from the match", my concept is "match the longest string that does not match *meta".

Both are reasonable concepts of how things might work but experimentation on bash shows it behaving according to the latter.
 
1 members found this post helpful.
Old 02-22-2012, 04:39 PM   #5
romagnolo
Member
 
Registered: Jul 2009
Location: Montaletto
Distribution: Debian GNU/Linux
Posts: 107

Original Poster
Rep: Reputation: 5
Thank you catkin.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Bash - add Pattern in Front of Regex danifunny Programming 5 11-04-2010 10:46 AM
Does bash shell parameter expansion run the risk of overflowing command line buffer? david1941 Linux - Software 1 04-28-2009 10:13 PM
LXer: Bash Parameter Expansion LXer Syndicated Linux News 0 10-02-2008 03:40 PM
Variable expansion inside of a bash script! A.S.Q. Linux - Newbie 4 09-29-2006 09:09 AM
linux bash - how to use a dynamic parameter in shell parameter expansion expression nickleus Linux - General 2 08-21-2006 04:54 AM

LinuxQuestions.org > Forums > Linux Forums > Linux - General

All times are GMT -5. The time now is 09:50 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration