Pratik
Pratik

Reputation: 1216

shell command to remove characters after a special character in bash/shell

I have filename

hello_1.0_25.tgz
a_hello_1.25.6_154.tgz
<name>_<name1>.tgz

The output which i need is

hello_1.0
a_hello_1.25.6
<name>

How can i get string before special character _ in bash (or) shell?

Upvotes: 0

Views: 113

Answers (7)

Jotne
Jotne

Reputation: 41446

This awk should do:

awk  -F_ '{$NF="";sub(/_$/,"")}1' OFS=_ file
hello_1.0
a_hello_1.25.6
<name>

-F_ Sets Field Separator to _
$NF="" Removes last field.
sub(/_$/,"") Removes last filed separator.
1 Prints out all lines.

Upvotes: 0

Tom Fenech
Tom Fenech

Reputation: 74596

In bash, this is easy:

$ f=hello_1.0_25.tgz
$ echo "${f%_*}"
hello_1.0

${f%_*} simply removes the _ and anything after it from the end of the variable f.

This is more concise than other approaches that use external tools and also saves using an extra process when one isn't needed.

more tips on string manipulation in bash

Upvotes: 4

gniourf_gniourf
gniourf_gniourf

Reputation: 46813

With Bash regular expressions:

$ f=hello_1.0_25.tgz
$ if [[ $f =~ (.*)_.*\.tgz$ ]]; then echo "${BASH_REMATCH[1]}"; fi
hello_1.0

Upvotes: 0

Karthikeyan.R.S
Karthikeyan.R.S

Reputation: 4041

Try this one.

sed 's/\(.*\)_\.*/\1/g' file_name

Upvotes: 0

David C. Rankin
David C. Rankin

Reputation: 84531

A slight variation on substring extraction:

$ m="a_hello_1.25.6_154.tgz"
$ echo "${m/%_${m/#*_/}/}"
$ a_hello_1.25.6

Which basically says ${m/#*_/} find the text following the last _ = 154.tgz (call it stuff); and then remove it, preceded by an underscore, from the backend of the string ${m/%_stuff/}. For a complete expression of ${m/%_${m/#*_/}/}.

Upvotes: 0

nu11p01n73R
nu11p01n73R

Reputation: 26667

Something like

sed -r 's/(.*)_.*/\1/'

Test

$ echo "hello_1.0_25.tgz" | sed -r 's/(.*)_.*/\1/'
hello_1.0
$ echo "a_hello_1.25.6_154.tgz" | sed -r 's/(.*)_.*/\1/'
a_hello_1.25.6
$ echo "<name>_<name1>.tgz" | sed -r 's/(.*)_.*/\1/'
<name>

What it does?

  • s substitute command

  • (.*) matches anything till the last _ . Saved in \1

  • _.* matches _ followed by the rest

  • /\1/ replaced with \1, first capture group

OR

sed -r 's/_[^_]+$//'

Test

$ echo "hello_1.0_25.tgz" | sed -r 's/_[^_]+$//'
hello_1.0
$ echo "a_hello_1.25.6_154.tgz"  | sed -r 's/_[^_]+$//'
a_hello_1.25.6
$ echo "<name>_<name1>.tgz"   | sed -r 's/_[^_]+$//'
<name>

What it does?

  • [^_]+ Matches anything other than _. + quantifes the previous pattern one or more times

  • $ matches the end of the line

  • // replaced with empty

Upvotes: 2

Kent
Kent

Reputation: 195029

this sed line should do:

sed 's/_[^_]*$//' 

little test with your example:

kent$  cat f
hello_1.0_25.tgz
a_hello_1.25.6_154.tgz
<name>_<name1>.tgz

kent$  sed 's/_[^_]*$//' f
hello_1.0
a_hello_1.25.6
<name>

awk can do it for sure too:

kent$  awk -F_ -v OFS="_" 'NF--' f
hello_1.0
a_hello_1.25.6
<name>

or grep if you like:

kent$  grep -Po '.*(?=_[^_]*$)' f
hello_1.0
a_hello_1.25.6
<name>

and @Tom Fenech 's bash way is nice too.

Upvotes: 1

Related Questions