Posting my comment as an answer as requested by @jared_mamrot.
You can manually type out the set of punctuation, excluding _
, that you want to delete. I took my punctuation set from GNU docs on [:punct:]
:
‘[:punct:]’ Punctuation characters; in the ‘C’ locale and ASCII
character encoding, this is ! " # $ % & ' ( ) * + , - . / : ; < = > ?
@ [ \ ] ^ _ ` { | } ~.
You can also look at POSIX docs which says the character classes depend on locale:
punct <exclamation-mark>;<quotation-mark>;<number-sign>;\
<dollar-sign>;<percent-sign>;<ampersand>;<apostrophe>;\
<left-parenthesis>;<right-parenthesis>;<asterisk>;\
<plus-sign>;<comma>;<hyphen>;<period>;<slash>;\
<colon>;<semicolon>;<less-than-sign>;<equals-sign>;\
<greater-than-sign>;<question-mark>;<commercial-at>;\
<left-square-bracket>;<backslash>;<right-square-bracket>;\
<circumflex>;<underscore>;<grave-accent>;<left-curly-bracket>;\
<vertical-line>;<right-curly-bracket>;<tilde>
$ echo 'abcd_!"#$%()*+,-./:;<=>?@][\\^`{}|~'"'" | tr -d '!"#$%()*+,-./:;<=>?@][\\^`{}|~'"'"
abcd_
The set of characters in the tr
command should be straightforward except for backslash, \\
, which has been escaped for tr
, and single quote, "'"
, which is being concatenated as a string quoted in double quotes, since you can't escape a single quote within single quotes.
I do prefer using @jared_marmot's complement solution, if possible, though. It is much neater.