16

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?

  • 1
    There are many ways to manipulate strings. Is there a specific reason for using sed ? – don_crissti Apr 7 '15 at 23:17
  • @don_crissti No reason, apart from lack of experience. Any alternatives are welcome... – 3kstc Apr 7 '15 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 Apr 7 '15 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 Apr 8 '15 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)}' – don_crissti Apr 8 '15 at 0:49
23

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.

| improve this answer | |
10
$ 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).

| improve this answer | |
  • 1
    Could you please explain the 's/^.\{3\}//' and 's/.\{4\}$//' as I am still learning sed, many thanks – 3kstc Apr 7 '15 at 23:18
  • @3kstc: Please check the edits – heemayl Apr 7 '15 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 Apr 8 '15 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. – Scott Jun 7 '18 at 21:20
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).

| improve this answer | |
  • @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. – Scott Jun 7 '18 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. – Stéphane Chazelas Jun 7 '18 at 21:39
2

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
| improve this answer | |
  • This only answers half of the question. – Kusalananda Dec 30 '19 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. – aexl Dec 31 '19 at 8:15
  • This solved my problem better than the rest of answers. – Alberto Salvia Novella Apr 24 at 10:53
0

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
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.