3

Greetings!

This are well know Bash parameter expansion patterns:

${parameter#word}, ${parameter##word}

and

${parameter%word}, ${parameter%%word}

I need to chop one part from the beginning and anoter part from the trailing of the parameter. Could you advice something for me please?

Andrey Kazak
  • 161
  • 1
  • 2
  • 5
  • you would get a lot better answers if you provide sample input and desired output – SiegeX Jun 16 '10 at 07:41
  • I have a path "/.../ABC/abc.txt" and I want to get name of the host folder for file "abc.txt", which in this example is "ABC". – Andrey Kazak Jun 16 '10 at 08:28

4 Answers4

1

If you're using Bash version >= 3.2, you can use regular expression matching with a capture group to retrieve the value in one command:

$ path='/xxx/yyy/zzz/ABC/abc.txt'
$ [[ $path =~ ^.*/([^/]*)/.*$ ]]
$ echo ${BASH_REMATCH[1]}
ABC

This would be equivalent to:

$ path='/xxx/yyy/zzz/ABC/abc.txt'
$ path=$(echo "$path" | sed 's|^.*/\([^/]*\)/.*$|\1|p')
$ echo $path
ABC
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
0

I don't know that there's an easy way to do this without resorting to sub-shells, something you probably want to avoid for efficiency. I would just use:

> xx=hello_there
> yy=${xx#he}
> zz=${yy%re}
> echo ${zz}
llo_the

If you're not fussed about efficiency and just want a one-liner:

> zz=$(echo ${xx%re} | sed 's/^he//')
> echo ${zz}
llo_the

Keep in mind that this second method starts sub-shells - it's not something I'd be doing a lot of if your script has to run fast.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • Thank you! I'm currently usit the variation of your first method: DIRNAME="./ABC/abc.txt" DIRNAME=${DIRNAME#./} DIRNAME=${DIRNAME%/*.txt} But I don't understand why this method involves sub-shells... – Andrey Kazak Jun 16 '10 at 08:25
  • The first method doesn't involve sub-shells, it's only the second that does that. I'll clarify. – paxdiablo Jun 16 '10 at 08:29
  • Is it possible somehow to incorporate two lines of your first method to a one line? – Andrey Kazak Jun 16 '10 at 08:53
  • Yes: `yy=${xx#he} ; zz=${yy%re}` :-) If you mean a single _command_, no, I don't believe so but someone with more `bash` knowledge than me may come along later. – paxdiablo Jun 16 '10 at 09:02
0

This solution uses what Andrey asked for and it does not employ any external tool. Strategy: Use the % parameter expansion to remove the file name, then use the ## to remove all but the last directory:

$ path=/path/to/my/last_dir/filename.txt

$ dir=${path%/*}     

$ echo $dir
/path/to/my/last_dir

$ dir=${dir##*/}

$ echo $dir
last_dir
Hai Vu
  • 37,849
  • 11
  • 66
  • 93
0

I would highly recommend going with bash arrays as their performance is just over 3x faster than regular expression matching.

$ path='/xxx/yyy/zzz/ABC/abc.txt'
$ IFS='/' arr=( $path )
$ echo ${arr[${#arr[@]}-2]}
ABC

This works by telling bash that each element of the array is separated by a forward slash / via IFS='/'. We access the penultimate element of the array by first determining how many elements are in the array via ${#arr[@]} then subtracting 2 and using that as the index to the array.

SiegeX
  • 135,741
  • 24
  • 144
  • 154