Search code examples
stringbashshellawksh

What is the best way to do this string transformation in shell?


I am trying to write a shell script that will need to transform input of the following form:

foo/bar/baz/qux.txt
bar/baz/quz.txt
baz/quz/foo.txt

Into:

baz-qux
quz
foo

I.e. split on '/', drop the first 2 segments, drop the '.txt' and substitute remaining slashes for hyphens.

The substitution seems straightforward enough using tr:

paths=$(cat <<- EOF
foo/bar/baz/qux.txt
bar/baz/quz.txt
baz/quz/foo.txt
EOF
)

echo $paths | tr '/' '-' | tr '.txt' ' '

I've tried various forms of

cut -d '/' -f x

To get the necessary segments but am coming up short.

I'm a ruby guy so tempted to reach for my hammer and just use ruby:

lines.each { |s| s.split('/')[2..-1].join('-').split('.')[0] }

But deploying ruby for this one operation seems like it might be overkill. And I would like to improve my shell skills anyway so was wondering if there is a more elegant way anyone would recommend to do this in shell?

Thanks for any help


Solution

  • It can be done using bash parameter expansions:

    for name in foo/bar/baz/qux.txt bar/baz/quz.txt baz/quz/foo.txt; do
        new=${name#*/}   # drop the shortest prefix match for */, thus everything up to first /
        new=${new#*/}    # repeat, dropping the second segment
        new=${new%.txt}  # drop shortest suffix match for .txt
        new=${new//\//-} # convert any remaining slashes
        echo "$new"
    done
    

    Gives:

    baz-qux
    quz
    foo
    

    These are all bash shell built-in constructs, so no external processes like cut, sed or tr required.