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.