31

I have a string that I would like to manipulate. The string is H08W2345678 how would I be able to manipulate it so the output is just W2345678?

Similarly if the I wanted to drop the last 4 characters from H08W2345678 so that I get H08W234 how would I do this?

5
  • 1
    There are many ways to manipulate strings. Is there a specific reason for using sed ? Commented Apr 7, 2015 at 23:17
  • @don_crissti No reason, apart from lack of experience. Any alternatives are welcome...
    – 3kstc
    Commented Apr 7, 2015 at 23:20
  • @don_crissti, the story: from a filtered down CSV file, I take one of the parameters from a line which is H08W2345678 and need to manipulate it to W2345678 This value with other datum will be put into an email sent off. Thie emailing will be undertaken with cron.
    – 3kstc
    Commented Apr 7, 2015 at 23:33
  • @don_crissti awking it. I create an array and then modify each of the element within the array (all differently - i.e change the Epoch timestaimp in seconds to a date etc.)
    – 3kstc
    Commented Apr 8, 2015 at 0:33
  • 2
    You can do stuff like that with awk: printf %s\\n "XX,H08W2345678,YY" | awk -F, '{print substr($2, 4); print substr($2, 1, length($2)-4)}' Commented Apr 8, 2015 at 0:49

5 Answers 5

48

Just using bash (or ksh93 where that syntax comes from or zsh):

string="H08W2345678"

echo "${string:3}"
W2345678

echo "${string:0:-4}"
H08W234

See the Wooledge wiki for more on string manipulation.

3
17
$ echo "H08W2345678" | sed 's/^.\{3\}//'
W2345678

sed 's/^.\{3\}//' will find the first three characters by ^.\{3\} and replace with blank. Here ^. will match any character at the start of the string (^ indicates the start of the string) and \{3\} will match the the previous pattern exactly 3 times. So, ^.\{3\} will match the first three characters.

$ echo "H08W2345678" | sed 's/.\{4\}$//'
H08W234

Similarly, sed 's/.\{4\}$//' will replace the last four characters with blank ($ indicates the end of the string).

4
  • 1
    Could you please explain the 's/^.\{3\}//' and 's/.\{4\}$//' as I am still learning sed, many thanks
    – 3kstc
    Commented Apr 7, 2015 at 23:18
  • @3kstc: Please check the edits
    – heemayl
    Commented Apr 7, 2015 at 23:21
  • 1
    For just a few characters, I'd use ... instead of .\{3\} since (to me) it's easier to read: sed -e 's/^...//' -e 's/....$//' or in a single expression with alternation: sed -r 's/^...|....$//g'. If it were more than a few characters to delete, then I'd use the /.\{17}\/ expression instead of /.............../.
    – Johnny
    Commented Apr 8, 2015 at 1:06
  • This will behave badly if the string is -e or -n. Of course, the meaning of “drop the last 4 characters” is undefined for a string shorter than 4 characters, but, if somebody wanted to adapt this to drop the first or last one character, it could blow up. Commented Jun 7, 2018 at 21:20
5

How to 'drop'/delete characters from in front of a string?

I have a string that I would like to manipulate. The string is H08W2345678 how would I be able to manipulate it so the output is just W2345678?

echo "H08W2345678" | cut -c 4-

Similarly if the I wanted to drop the last 4 characters from H08W2345678 so that I get H08W234 how would I do this?

echo "H08W2345678" | cut -c -7
4
  • This only answers half of the question.
    – Kusalananda
    Commented Dec 30, 2019 at 11:59
  • I believe your downvote is unfair. This half answers the question I had when I googled posix remove first characters and this page showed up in search results. Moreover, this page title covers only that exact half of the question. I came back and contributed when I found the solution I liked - I think for that job cut is much more elegant than whatever else is on this page. Commented Dec 31, 2019 at 8:15
  • This solved my problem better than the rest of answers. Commented Apr 24, 2020 at 10:53
  • This answer is acceptable if you know the length of the string. If you don't you have to do extra math to calculate how to cut the characters at the end.
    – givanse
    Commented Sep 17, 2022 at 6:46
2

If you have a file in which every line is an eleven-character (or whatever) string that you want to chop up, sed is the tool to use.  It’s fine for manipulating a single string, but it’s overkill.  For a single string, Jason’s answer is probably the best, if you have access to bash version 4.2 or higher.  However, the ${parameter:offset} and ${parameter:offset:length} syntaxes appear to be unique to bash (well, bash, ksh93, mksh, and zsh) — I don’t see them in The Open Group Base Specifications for Shell Command Language.  If you’re stuck with a POSIX-compliant shell that doesn’t support substring expansion (extraction), you can use

$ printf "%s\n" "${string#???}"
W2345678

$ printf "%s\n" "${string%????}"
H08W234

using printf instead of echo to guard against strings like abc-e, where, when you drop the first three characters, you are left with -e (and echo -e doesn’t do what you would want).

And, if you’re not using a Bourne-family shell at all (or you’re using an ancient, pre-POSIX system), these should still work:

$ expr " $string" : ' ...\(.*\)'
W2345678

$ expr " $string" : ' \(.*\)....'
H08W234

The extra leading space is to avoid problems with values of $string that are actual expr operators (e.g., +, /, index or match) or options (e.g., --, --help or --version).

2
  • @Stéphane Chazelas: (1) Thanks for reminding me of a pitfall that I knew about 40 years ago and somehow managed to forget. (2) I always used to solve this with X; e.g., expr "X$string" : 'X...\(.*\)'.  IMO, that’s easier to read and understand.  Is there any problem with that, or any reason to prefer a space? (3) Today I learned that expr + "$string" : '...\(.*\)' now works.  I don’t remember that from 40 years ago; is it sufficiently widely used to be safe to recommend? (4) You missed a note on jasonwryan’s answer and a nit-pick on heemayl’s answer. Commented Jun 7, 2018 at 21:20
  • AFAIK, that expr + is GNU only (won't work on Solaris nor FreeBSD AFAICS). I use space instead of x as it's less likely that some expr implementation would have operators that start with space than with x and also because it's less likely there be collating elements that start with space than with x. But then I realise it's probably not a good choice for expr " $a" "<" " $b" for string comparison as some implementations end up doing numerical comparison when $a/$b look like numbers. Maybe expr "@@$a"... or expr "x $a" could be safer. Commented Jun 7, 2018 at 21:39
1

With:

string="H08W2345678"

Matching 3 or 4 characters seems simple (for most shells):

$ printf '%s\t%s\n' "${string#???}" "${string%????}"
W2345678      H08W234

For the older shells (like the Bourne shell), use:

$ string=H08W2345678

$ expr " ${string}" : " ...\(.*\)"
W2345678

$ expr " ${string}" : " \(.*\)...." '
H08W234

If it is needed a numeric count of characters, use:

$ expr " ${string}" : " .\{3\}\(.*\)"
W2345678

$ expr " ${string}" : " \(.*\).\{4\}" '
H08W234

Of course, those regex work also with sed, awk and bash 3.0+:

$ echo "$string" | sed 's/^.\{3\}//'
W2345678

$ echo "$string" | sed 's/.\{4\}$//'
H08W234

$ echo "$string" | awk '{sub(/^.{3}/,"")}1'
W2345678

$ echo "$string" | awk '{sub(/.{4}$/,"")}1'
H08W234

$ r='^.{3}(.*)$'; [[ $a =~ $r ]] && echo "${BASH_REMATCH[1]}"
W2345678

$ r='^(.*).{4}$'; [[ $a =~ $r ]] && echo "${BASH_REMATCH[1]}"
H08W234

You must log in to answer this question.

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