2

I want to delete some punctuation marks except specific lines. Like, I want to sed just remove these , : { } [ ] marks, but not at lines 1, 4-7, 38, 39. How can I achive that?

1
  • 2
    To help community help you and to make your question clearer, post samples of your input and desired output. In addition to what you already has done and the problem with your code.
    – Eng7
    Dec 11 '19 at 9:20
3

You could add one block after another you want to exclude from your substitution:

sed -e '1!{ 4,7!{ 38,39! s/[][,:{}]//g ;}' -e '}' file_in > file_out

Example (with different ranges to keep it short):

$ printf 'line %s , : { } [ ]\n' {1..10} | sed -e '1!{ 4,6!{ 8,9! s/[][,:{}]//g ;}' -e '}'
line 1 , : { } [ ]
line 2
line 3
line 4 , : { } [ ]
line 5 , : { } [ ]
line 6 , : { } [ ]
line 7
line 8 , : { } [ ]
line 9 , : { } [ ]
line 10
0
1

One approach is to use the b command on the lines you want to preserve first:

sed -e 1b -e 4,7b -e 38,39b -e 's/[][,:{}]//g'

Or:

sed '
  1     b
  4,7   b
  38,39 b
  s/[][,:{}]//g'

With the GNU implementation of sed, you can also write it:

sed '1b;4,7b;38,39b;s/[][,:{}]//g'
1

Using Perl, we can use the line ranges to deselect via the very low precedence or operator.

$ perl -pe '1..1 or 4..7 or 38..39 or tr/][,:}{//d'

Another way:

perl -pe 'grep { $. == $_ } (1, 4..7, 38..39) or tr/[]{},://d' 
2
  • I don't know much perl but, is that necessarily to write 1..1 instead of just 1 there? Dec 11 '19 at 16:19
  • 1
    @afshin The solitary 1 will not work here bcoz it will short circuit the entire boolean condition. What we have here is the range operator .. and which needs two operands. Alternatively, we can use $. == 1 in place of the 1..1 Dec 14 '19 at 8:42
0

You can try something like that with awk

awk '{ if (NR !~ /^(1|[4-7]|38|39)$/) { gsub(/[,:\{\}\[\]]+/,"",$0) }; print }' your-file.txt

if NR (line number does not match 1 or 4 to 7 or 38 or 39, then replace signs with "nothing". Then print line

5
  • a golfed version would be awk -F"[][,:}{]" 'NR !~ /^(1|[4-7]|3(8|9))$/{$1=$1}1' infile Dec 11 '19 at 10:01
  • like it but I don't understand how this works. Could you please explain this $1=$1 ?
    – darxmurf
    Dec 11 '19 at 14:42
  • In awk, with -F option we can specify what delimiter to use as field seperator which default for awk is whitespaces (Tab and/or Space), so for example with -F, we tells awk field seperator is , comma. this option also supports multiple seperators to define that's why default is Tab and Space, and that's we should define those in brackets like -F"[...]", so now you got what -F"[][,:}{]" is doing, right? <...> Dec 11 '19 at 16:13
  • <...> with $1=$1 we set $1 value to itself (that we can also do $2=$2, or $3=$3, or ...) and that cause no changes in field's value of course but awk do re-evaluation of $0 based on OFS which we didn't set that, so it will cause to remove all those delimiters from the line without Output Field Seperator; and that 1 at the end is an awk idiom to print the line, same as { $1=$1; print } Dec 11 '19 at 16:13
  • Yep, just found out this trick :-) pretty fun, like it! Thanks
    – darxmurf
    Dec 11 '19 at 16:35

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.