133

I have a string like that:

|abcdefg|

And I want to get a new string called in someway (like string2) with the original string without the two | characters at the start and at the end of it so that I will have this:

abcdefg

Is that possible in bash?

10 Answers 10

150

You can do

string="|abcdefg|"
string2=${string#"|"}
string2=${string2%"|"}
echo $string2

Or if your string length is constant, you can do

string="|abcdefg|"
string2=${string:1:7}
echo $string2

Also, this should work

echo "|abcdefg|" | cut -d "|" -f 2

Also this

echo "|abcdefg|" | sed 's/^|\(.*\)|$/\1/'
5
  • 2
    and also awk -F\| '{ print $2 }' <<<"|string|"
    – enzotib
    Commented Dec 23, 2011 at 17:45
  • 1
    @enzotib You always have cool awk solutions. I need to learn awk. Commented Dec 23, 2011 at 18:36
  • 3
    and also IFS='|' read string2 <<< $string :)
    – arrange
    Commented Dec 23, 2011 at 20:38
  • 20
    and also, in bash 4.2 and newer, "${string:1:-1}"
    – geirha
    Commented Jul 5, 2012 at 12:52
  • Read under the "parameter expansion" section in man bash.
    – Nemo
    Commented Sep 5, 2012 at 20:44
82

Here's a solution that is independent of the length of the string (bash):

string="|abcdefg|"
echo "${string:1:${#string}-2}"
66

Going off a few posts listed here it seems the simplest way to do it is:

string="|abcdefg|"
echo ${string:1:-1}

edit: works on ubuntu with bash 4.2; does not work on centOS with bash 4.1

2
  • 3
    This should be have been the top answer. You won't find any bash 4.1 on modern systems. :) Commented Jul 9, 2020 at 14:17
  • The first 1 in this answer represents how many characters are dropped from the start, The second 1 represents how many characters are dropped from the end. So echo echo ${string:2:-3} would yield output:bcde.
    – a.t.
    Commented Feb 19, 2021 at 17:48
33

Another way is to use head & tail commands:

$ echo -n "|abcdefg|" | tail -c +2 | head -c -2
abcdefg
4
  • 1
    I had a string of [something something] with a goal to cut brackets, so echo "[something something]" | tail -c +2 | head -c -2 worked out. Thanks for a tip!
    – Ain Tohvri
    Commented Dec 13, 2015 at 15:36
  • 2
    Obviously, it should be echo -n "|abcdefg|" | tail -c +2 | head -c -1. I'm not sure why my edits were rejected... It's very sad, to be honest. See man head for more info Commented Mar 27, 2019 at 13:11
  • Best answer, does what was requested with the most simple tools and is not dependent on extra factors like char is x, y or z.
    – progonkpa
    Commented Jan 17, 2023 at 13:13
  • Not working on the latest shell editions (at least on mac...). I get head: illegal byte count -- -2 Commented Sep 7, 2023 at 10:03
11

You can also use sed to remove the | not just referencing the symbol itself but using positional references as in:

$ echo "|abcdefg|" | sed 's:^.\(.*\).$:\1:'
abcdefg

Where ':' are the delimiters (you can replace them with / or any character not in the query, any sign following the s will do it) Here ^ (caret) means at the beginning of the input string and $ (dollar) means at the end. The . (point) that it's after the caret and the one that it's before the dollar sign represents a single character. So in other words we are deleting the first and last characters. Take in mind this will delete any characters even if | it's not present in the string.

EX:

$ echo "abcdefg" | sed 's:^.\(.*\).$:\1:'
bcdef
10

And another one:

string="|abcdefg|"
echo "${string//|/}"
3

shell function

A bit more verbose approach, but works on any sort of first and last character, doesn't have to be the same. Basic idea is that we are taking a variable, reading it character by character, and appending only those we want to a new variable

Here's that whole idea formatted into a nice function

crop_string_ends() {
    STR="$1" 
    NEWSTR="" 
    COUNT=0 
    while read -n 1 CHAR 
    do
        COUNT=$(($COUNT+1)) 
        if [ $COUNT -eq 1 ] || [ $COUNT -eq ${#STR} ] 
        then
            continue 
        fi 
        NEWSTR="$NEWSTR"$CHAR 
    done <<<"$STR" 
    echo $NEWSTR 
}

And here is that same function in action:

$> crop_string_ends "|abcdefg|"                                                                                       
abcdefg
$> crop_string_ends "HelloWorld"                                                                                      
elloWorl

Python

>>> mystring="|abcdefg|"
>>> print(mystring[1:-1])
abcdefg

or on command line:

$ python -c 'import sys;print sys.stdin.read()[1:-2]' <<< "|abcdefg|"                                             
abcdefg

AWK

$ echo "|abcdefg|" | awk '{print substr($0,2,length($0)-2)}'                                                      
abcdefg

Ruby

$ ruby -ne 'print $_.split("|")[1]' <<< "|abcdefg|"                                                               
abcdefg
2

Small and universal solution:

expr "|abcdef|" : '.\(.*\).'

Special in this case and allowing that the '|' character may be there or not:

expr "|abcdef" : '|*\([^|]*\)|*'
2
$ string="|abcdefg|"
$ string="${string#?}" && string="${string%%?}"
$ echo "$string"
abcdefg

From http://tldp.org/LDP/abs/html/parameter-substitution.html

${var#Pattern}
Remove from $var the shortest part of $Pattern that matches the front end of $var.
${var%%Pattern}
Remove from $var the longest part of $Pattern that matches the back end of $var.

2

You can use this:

echo "|abcdefg|" | tr -d "|"

With this line of code, you remove all |->(pipe) in the string.

2
  • 1
    I needed to remove dbl-quotes from a grepd string (e.g. "IPVanish-nyca20-VPN"). The | tr -d "\"" worked like a charm. +1
    – Seamus
    Commented Mar 5 at 21:05
  • Surprising that this is the only answer here that recommends tr; even more surprising that this is the first answer - after 13 years - to use tr in the answer. Hmmm - maybe that says something about Ubuntu (or its users) lol Anyway, here's more on tr from Dave McKay.
    – Seamus
    Commented Mar 5 at 22:36

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .