update
#!/bin/bash
case "$#" in
2) s1="$1"; s2="$2";;
*) echo "$0 {save|del} <key>"
exit 1;;
esac
case "$s1" in
save) v=-v ;;
del) v= ;;
esac
sed -in "$(cut -c -${#s2} x | grep $v -F -n "$s2" | sed "s/:.*/ d;/" )" test.txt
This handles complex leading metacharacter strings. :)
old stuff
I'm copying test.txt to an editable temp so that I have a consistent source for testing. I also put the args on the commandline because I got tired of typing them separately and wanted to test in a loop.
Yes, I know you were reading stdin, but you had that part working fine and can put it back if you like.
Oh, and I like case statements. :)
#!/bin/bash
case "$#" in
2) s1="$1"; s2="$2";;
*) echo "$0 {save|del} <key>"
exit 1;;
esac
# make a var that is $s2 but with these metacharacters removed
tst="$( echo "$s2" | tr -d "[/.;\][}{!#^&*)(-?\\\\]" )"
case "$tst" in
$s2) q='' ;; # if it's the same, no metas to worry about
*) q="\\" ;; # else we need to quote the character
esac
cat test.txt >| edited
case "$s1" in
save) sed -in "/^$q$s2/!d" edited ;;
del) sed -in "/^$q$s2/d" edited ;;
esac
cat edited
This works even if you use a relevant metacharacter such as /
or .
, because it quotes it. The code doesn't need to exclude any characters from the list of options.
My source input -
$: cat test.txt
foo1234567890qwertyuiopasdfghjklzxcvbnm
bar1234567890qwertyuiopasdfghjklzxcvbnm
/1234567890qwertyuiopasdfghjklzxcvbnm
&1234567890qwertyuiopasdfghjklzxcvbnm
|1234567890qwertyuiopasdfghjklzxcvbnm
.1234567890qwertyuiopasdfghjklzxcvbnm
?1234567890qwertyuiopasdfghjklzxcvbnm
My test examples:
$: for cmd in save del
do for k in foo \/ \. \? \& \|
do echo "
===== $cmd $k
"
script $cmd "$k"
done
done
===== save foo
foo1234567890qwertyuiopasdfghjklzxcvbnm
===== save /
/1234567890qwertyuiopasdfghjklzxcvbnm
===== save .
.1234567890qwertyuiopasdfghjklzxcvbnm
===== save ?
?1234567890qwertyuiopasdfghjklzxcvbnm
===== save &
&1234567890qwertyuiopasdfghjklzxcvbnm
===== save |
|1234567890qwertyuiopasdfghjklzxcvbnm
===== del foo
bar1234567890qwertyuiopasdfghjklzxcvbnm
/1234567890qwertyuiopasdfghjklzxcvbnm
&1234567890qwertyuiopasdfghjklzxcvbnm
|1234567890qwertyuiopasdfghjklzxcvbnm
.1234567890qwertyuiopasdfghjklzxcvbnm
?1234567890qwertyuiopasdfghjklzxcvbnm
===== del /
foo1234567890qwertyuiopasdfghjklzxcvbnm
bar1234567890qwertyuiopasdfghjklzxcvbnm
&1234567890qwertyuiopasdfghjklzxcvbnm
|1234567890qwertyuiopasdfghjklzxcvbnm
.1234567890qwertyuiopasdfghjklzxcvbnm
?1234567890qwertyuiopasdfghjklzxcvbnm
===== del .
foo1234567890qwertyuiopasdfghjklzxcvbnm
bar1234567890qwertyuiopasdfghjklzxcvbnm
/1234567890qwertyuiopasdfghjklzxcvbnm
&1234567890qwertyuiopasdfghjklzxcvbnm
|1234567890qwertyuiopasdfghjklzxcvbnm
?1234567890qwertyuiopasdfghjklzxcvbnm
===== del ?
foo1234567890qwertyuiopasdfghjklzxcvbnm
bar1234567890qwertyuiopasdfghjklzxcvbnm
/1234567890qwertyuiopasdfghjklzxcvbnm
&1234567890qwertyuiopasdfghjklzxcvbnm
|1234567890qwertyuiopasdfghjklzxcvbnm
.1234567890qwertyuiopasdfghjklzxcvbnm
===== del &
foo1234567890qwertyuiopasdfghjklzxcvbnm
bar1234567890qwertyuiopasdfghjklzxcvbnm
/1234567890qwertyuiopasdfghjklzxcvbnm
|1234567890qwertyuiopasdfghjklzxcvbnm
.1234567890qwertyuiopasdfghjklzxcvbnm
?1234567890qwertyuiopasdfghjklzxcvbnm
===== del |
foo1234567890qwertyuiopasdfghjklzxcvbnm
bar1234567890qwertyuiopasdfghjklzxcvbnm
/1234567890qwertyuiopasdfghjklzxcvbnm
&1234567890qwertyuiopasdfghjklzxcvbnm
.1234567890qwertyuiopasdfghjklzxcvbnm
?1234567890qwertyuiopasdfghjklzxcvbnm
This still leaves the problem of the case where the leading string consists of multiple metacharacters... I'm thinking on that one...
So, alternate version:
#!/bin/bash
case "$#" in
2) s1="$1"; s2="$2";;
*) echo "$0 {save|del} <pattern>"
exit 1;;
esac
cat test.txt >| edited
case "$s1" in
save) act="!d" ;;
del) act="d" ;;
esac
sed -in "$s2$act" edited
cat edited
This means you have to put the search pattern in on the command line (correctly), but then you can handle any weirdness you like. For example, I added a line -
/.?1234567890qwertyuiopasdfghjklzxcvbnm
and ran it as script save "/^[/][.][?]/"
, which worked fine.