Peaceful
Peaceful

Reputation: 5450

Remove suffix as well as prefix from path in bash

I have filepaths of the form:

../healthy_data/F35_HC_532d.dat

I want to extract F35_HC_532d from this. I can remove prefix and suffix from this filename in bash as:

for i in ../healthy_data/*; do echo ${i#../healthy_data/}; done # REMOVES PREFIX

for i in ../healthy_data/*; do echo ${i%.dat}; done # REMOVES SUFFIX

How can I combine these so that in a single command I would be able to remove both and extract only the part that I want?

Upvotes: 2

Views: 3179

Answers (7)

Georg Lehner
Georg Lehner

Reputation: 49

In your specific example the prefix stems from the fact that the files are located in a different directory. You can get rid of the prefix by cding in this case.

(cd ../healthy_data ; for i in *; do echo ${i%.dat}; done)

The (parens) invoke a sub shell process and your current shell stays where it is. If you don't want a sub shell you can cd back easily:

cd ../healthy_data ; for i in *; do echo ${i%.dat}; done; cd -

Upvotes: -1

cicolus
cicolus

Reputation: 827

If you are on zsh, one way to achieve this without the need for defining another variable is

for i in ../healthy_data/*; do echo "${${i#../healthy_data/}%.dat}"; done

This removes prefix and suffix in one step.

Upvotes: 0

John B
John B

Reputation: 3646

You can't strip both a prefix and suffix in a single parameter expansion.

However, this can be accomplished in a single loop using parameter expansion operations only. Just save the prefix stripped expansion to a variable and use expansion again to remove its suffix:

for file in ../healthy_data/*; do
    prefix_stripped="${file##*\/healthy_data\/}"
    echo "${prefix_stripped%.dat}"
done

Upvotes: 0

hek2mgl
hek2mgl

Reputation: 157967

If all files end with .dat (as you confirmed) you can use the basename command:

basename -s .dat /path/to/files/*

If there are many(!) of those files, use find to avoid an argument list too long error:

find /path/to/files -maxdepth 1 -name '*.dat' -exec basename -s .dat {} +

For a shell script which needs to deal if any number of .dat files use the second command!

Upvotes: 1

Walter A
Walter A

Reputation: 20002

Do you count this as one step?

for i in ../healthy_data/*; do 
   sed 's#\.[^.]*##'<<< "${i##*/}"
done

Upvotes: 0

Inian
Inian

Reputation: 85580

If you can use Awk, it is pretty simple,

for i in ../healthy_data/*
do 
    stringNeeded=$(awk -F/ '{split($NF,temp,"."); print temp[1]}' <<<"$i")
    printf "%s\n" "$stringNeeded"
done

The -F/ splits the input string on / character, and $NF represents the last field in the string in that case, F35_HC_532d.dat, now the split() function is called with the de-limiter . to extract the part before the dot.

The options/functions in the above Awk are POSIX compatible.


Also bash does not support nested parameter expansions, you need to modify in two fold steps something like below:-

tempString="${i#*/*/}"
echo "${tempString%.dat}"

In a single-loop,

for i in ../healthy_data/*; do tempString="${i#*/*/}"; echo "${tempString%.dat}" ; done 

The two fold syntax here, "${i#*/*/}" part just stores the F35_HC_532d.dat into the variable tempString and in that variable we are removing the .dat part as "${tempString%.dat}"

Upvotes: 1

anubhava
anubhava

Reputation: 785126

You can use BASH regex for this like this and print captured group #1:

for file in ../healthy_data/*; do
    [[ $file =~ .*/([_[:alnum:]]+)\.dat$ ]] && echo "${BASH_REMATCH[1]}"
done

Upvotes: 5

Related Questions