bitwise XOR a string in Bash - perl

I am trying to accomplish a work in Bash scripting. I have a string which i want to XOR with my key.
#!/bin/sh
PATH=/bin:/usr/bin:/sbin:/usr/sbin export PATH
teststring="abcdefghijklmnopqr"
Now how do i XOR the value of teststring and store it in a variable using bash?
Any help will be appreciated.
Basically i am trying to duplicate the result of follwing VB Script:
Function XOREncryption(CodeKey, DataIn)
Dim lonDataPtr
Dim strDataOut
Dim temp
Dim tempstring
Dim intXOrValue1
Dim intXOrValue2
For lonDataPtr = 1 To Len(DataIn) Step 1
'The first value to be XOr-ed comes from the data to be encrypted
intXOrValue1 = Asc(Mid(DataIn, lonDataPtr, 1))
'The second value comes from the code key
intXOrValue2 = Asc(Mid(CodeKey, ((lonDataPtr Mod Len(CodeKey)) + 1), 1))
temp = (intXOrValue1 Xor intXOrValue2)
tempstring = Hex(temp)
If Len(tempstring) = 1 Then tempstring = "0" & tempstring
strDataOut = strDataOut + tempstring
Next
XOREncryption = strDataOut
End Function

With the help of these hints i wrote this quickly script to complete Pedro's answer:
#!/bin/bash
function ascii2dec
{
RES=""
for i in `echo $1 | sed "s/./& /g"`
do
RES="$RES `printf \"%d\" \"'$i\"`"
done
echo $RES
}
function dec2ascii
{
RES=""
for i in $*
do
RES="$RES`printf \\\\$(printf '%03o' $i)`"
done
echo $RES
}
function xor
{
KEY=$1
shift
RES=""
for i in $*
do
RES="$RES $(($i ^$KEY))"
done
echo $RES
}
KEY=127
TESTSTRING="abcdefghijklmnopqr"
echo "Original String: $TESTSTRING"
STR_DATA=`ascii2dec "$TESTSTRING"`
echo "Original String Data: $STR_DATA"
XORED_DATA=`xor $KEY $STR_DATA`
echo "XOR-ed Data: $XORED_DATA"
RESTORED_DATA=`xor $KEY $XORED_DATA`
echo "Restored Data: $RESTORED_DATA"
RESTORED_STR=`dec2ascii $RESTORED_DATA`
echo "Restored String: $RESTORED_STR"
Result:
iMac:Desktop fer$ bash test.sh
Original String: abcdefghijklmnopqr
Original String Data: 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
XOR-ed Data: 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13
Restored Data: 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
Restored String: abcdefghijklmnopqr

If you decide to go for Perl one-liner, here is what I came up with
perl -e '#a=split("", $ARGV[0]); #b=split("", $ARGV[1]); print unpack "H2", chr(ord(shift #a)^ord(shift #b)) while #a; print "\n"' aab aaa
zip function in Perl 6 would do a better job...

yet another answer
function xenc {
local data=$1 key=$2
local _data _key ndata nkey count i _res
_data=($(eval printf "'%d '" $(printf "%s" "$data" | sed -e '$!N;${s/./"'"'"'&" /g;s/""/\\&/g}')))
_key=($(eval printf "'%d '" $(printf "%s" "$key" | sed '$!N;${s/./"'"'"'&" /g;s/""/\\&/g}')))
ndata=${#_data[#]} nkey=${#_key[#]}
(( count = ndata < nkey ? nkey : ndata ))
for ((i = 0; i < count; i++)); do
(( _res[i] = ${_data[i]:-0} ^ ${_key[i%nkey]:-0} ))
done
printf "\\\\\\%o" "${_res[#]}" | xargs printf
}
res=$(xenc abcdefghijklmnopqrstuvwxyz FLqFb8LU0TY)
xenc "$res" FLqFb8LU0TY

Bitwise exclusive-OR in BASH requires both operands to be numeric. Since there's no built-in way of getting the ordinal (ASCII) value of a character in bash, you'll need to use, say, Perl, to get that value.
Edit: as noted below, ord works on the first character of a string only.
let a=`perl -e 'print ord $_ for split //, $ARGV[0]' string`^123; echo $a
Of course, once you're in Perl, you might as well do it all there:
let a=`perl -e '$ordinal .= ord $_ for split //, $ARGV[0]; print $ordinal ^ $ARGV[1]' string 123`
Edit: it turns out you can grab the ordinal value of a string in BASH using printf. Simply prefix the string with '.
printf "%d" "'string"
So, in BASH only:
let a=$(printf "%d" "'string")^123; echo $a

The most similar transformation of only the function to bash would be:
(# means comment):
# Function XOREncryption(CodeKey, DataIn)
XOREncryption(){
local CodeKey=$1
local DataIn=$2
# Dim lonDataPtr strDataOut temp tempstring intXOrValue1 intXOrValue2
local lonDataPtr strDataOut temp tempstring intXOrValue1 intXOrValue2
# For lonDataPtr = 1 To Len(DataIn) Step 1
for (( lonDataPtr=0; lonDataPtr < ${#DataIn}; lonDataPtr++ )); do
#The first value to be XOr-ed comes from the data to be encrypted
# intXOrValue1 = Asc(Mid(DataIn, lonDataPtr, 1))
intXOrValue1=$( toAsc "${DataIn:$lonDataPtr:1}" )
echo "val1=$intXOrValue1 and=<${DataIn:$lonDataPtr:1}> and $(toAsc "${DataIn:$lonDataPtr:1}")"
#The second value comes from the code key
echo "Ptr=$lonDataPtr Mod=<$(( lonDataPtr % ${#CodeKey} ))>"
# intXOrValue2 = Asc(Mid(CodeKey, ((lonDataPtr Mod Len(CodeKey)) + 1), 1))
intXOrValue2=$( toAsc "${CodeKey:$(( lonDataPtr % ${#CodeKey} )):1}" )
echo "val1=$intXOrValue1 val2=<${CodeKey:$(( lonDataPtr % ${#CodeKey} )):1}> and |$intXOrValue2|"
# temp = (intXOrValue1 Xor intXOrValue2)
temp=$(( intXOrValue1 ^ intXOrValue2 ))
echo "temp=$temp"
# tempstring = Hex(temp)
tempstring=$(printf '%02x' "$temp")
echo "tempstring=$tempstring"
# strDataOut = strDataOut + tempstring
strDataOut+=$tempstring
echo
# Next
done
# XOREncryption = strDataOut
printf '%s\n' "$strDataOut"
# End Function
}
Removing the comments, and cleaning the code:
#!/bin/bash
Asc() { printf '%d' "'$1"; }
XOREncryption(){
local key=$1 DataIn=$2
local ptr DataOut val1 val2
for (( ptr=0; ptr < ${#DataIn}; ptr++ )); do
val1=$( Asc "${DataIn:$ptr:1}" )
val2=$( Asc "${key:$(( ptr % ${#key} )):1}" )
DataOut+=$(printf '%02x' "$(( val1 ^ val2 ))")
done
printf '%s\n' "$DataOut"
}
CodeKey="$1"
teststring="$2"
XOREncryption "$CodeKey" "$teststring"
Running it:
$ ./script.sh "123456789" "abcdefghijklmnopqr"
5050505050505050505b595f595b5947494b

woks in busybox(the paste could not receive two streams), also made the key to repeat
#!/bin/sh
numitems() { i=0;while read ITEM; do i=$(( $i + 1 )) ; done; echo $i; }
starmap() { while read ITEM; do $1 $ITEM; done; }
ord() { printf '%d\n' "'$1"; }
chr() { printf \\$(printf '%03o' $1); }
split_chars() { echo -n "$1" | sed 's/./&\n/g'; }
xor() { echo $(($1 ^ $2)); }
map_ord() { split_chars "$1" | starmap ord; }
encrypt()
{
KEY=$1;STR=$2;
while [ ${#KEY} -lt ${#STR} ]; do KEY="$KEY$KEY"; done; #make key longer then str
[ -e /tmp/paste_col1 ] && rm -rf /tmp/paste_col1
[ -e /tmp/paste_col1t ] && rm -rf /tmp/paste_col1t
[ -e /tmp/paste_col2 ] && rm -rf /tmp/paste_col2
map_ord "$KEY">/tmp/paste_col1t
map_ord "$STR">/tmp/paste_col2
head -n `wc -l /tmp/paste_col2 |sed -r 's|^([0-9]+)\s.*|\1|'` /tmp/paste_col1t>/tmp/paste_col1 #trim lines
[ -e /tmp/paste_col1t ] && rm -rf /tmp/paste_col1t
paste /tmp/paste_col1 /tmp/paste_col2 | starmap xor | starmap chr
[ -e /tmp/paste_col1 ] && rm -rf /tmp/paste_col1
[ -e /tmp/paste_col2 ] && rm -rf /tmp/paste_col2
echo
}
KEY="12345678"
TESTSTRING="abcdefghasdfasdfasfdas"
encrypt "$KEY" "$TESTSTRING"
ENC="`encrypt \"$KEY\" \"$TESTSTRING\"`"
encrypt "$KEY" "$ENC" # we should get $TESTSTRING again

Related

Returning from /bin/sh Functions

I've written a lot of Bash, but I now need to port some of my code into /bin/sh, and I'm observing strange behavior.
I have a function:
path_exists() {
path="$1" && shift
echo "$PATH" | tr ':' '\n' | while read path_entry ; do
test "$path" = "$path_entry" && return 0
done
return 1
}
I'm calling it like so:
if path_exists "/usr/bin" ; then
echo "it exists"
else
echo "it does not exist"
fi
If I run it with set -x, I see that return codes are overwritten:
+ path_exists /usr/bin
+ path=/usr/bin
+ shift
+ echo /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
+ tr : \n
+ read path_entry
+ test /usr/bin = /usr/local/sbin
+ read path_entry
+ test /usr/bin = /usr/local/bin
+ read path_entry
+ test /usr/bin = /usr/sbin
+ read path_entry
+ test /usr/bin = /usr/bin
+ return 0
+ return 1
+ echo it does not exist
Why is my function not respecting my return codes?
Why is my function not respecting my return codes?
Because pipe spawns a subshell, and you are only returning from a subshell. The same way:
echo | exit 0
will not exit your shell, only exit from | { the subshell on the right side of pipe; }. Or the same way func() { ( return 1; echo 123; ); return 0; }; func - the return 1 only affects the subshell.
The behavior is the same in ash, dash and in bash, but I think in POSIX sh it would be unspecified.
Aaaaanyway:
path_exists() {
case ":$PATH:" in
*:"$1":*) ;;
*) false; ;;
esac
}

posix sh: how to count number of occurrences in a string without using external tools?

In bash, it can be done like this:
#!/bin/bash
query='bengal'
string_to_search='bengal,toyger,bengal,persian,bengal'
delimiter='|'
replace_queries="${string_to_search//"$query"/"$delimiter"}"
delimiter_count="${replace_queries//[^"$delimiter"]}"
delimiter_count="${#delimiter_count}"
echo "Found $delimiter_count occurences of \"$query\""
Output:
Found 3 occurences of "bengal"
The caveat of course is that the delimiter cannot occur in 'query' or 'string_to_search'.
In POSIX sh, string replacement is not supported. Is there a way this can be done in POSIX sh using only shell builtins?
#!/bin/sh
query='bengal'
string_to_search='bengal,toyger,bengal,persian,bengal'
ct() (
n=0
IFS=,
q=$1
set $2
for t in "$#"; do
if [ "$t" = "$q" ]; then
n=$((n + 1))
fi
done
echo $n
)
n=$(ct "$query" "$string_to_search")
printf "found %d %s\n" $n $query
Though I'm not sure what the point is. If you've got a posix shell,
you also almost certainly have printf, sed, grep, and wc.
printf '%s\n' "$string_to_search" | sed -e 's/,/\n/g' | grep -Fx "$query" | wc -l
Think I got it...
#!/bin/sh
query='bengal'
string_to_search='bengal,toyger,bengal,persian,bengal'
i=0
process_string="$string_to_search"
while [ -n "$process_string" ]; do
case "$process_string" in
*"$query"*)
process_string="${process_string#*"$query"}"
i="$(( i + 1 ))"
;;
*)
break
;;
esac
done
echo "Found $i occurences of \"$query\""

Print all line between the search pattern into different files using perl or any method

Could someone help out on this
I want to print all line between the search pattern (START & END) to different files (new_file_name can be any incremental name provided)
But the search pattern repeats in file hence each time it finds the pattern it should dump the line b/w them into different files
The file is something like this
START --- ./body1/b1
##########################
123body1
abcbody1
##########################
END --- ./body1/b1
START --- ./body2/b2
##########################
123body2
defbody2
##########################
END --- ./body2/b2
perl solution,
perl -MFile::Basename -MFile::Path -ne '
($a) = /^START.+?(\S+)$/;
$b = /^END/;
$a..$b or next;
if ($a){ mkpath(dirname $a); open STDOUT,">",$a; }
$a||$b or print;
' file
Here is my awk solution:
# print_between_patterns.awk
/^START/ { filename = $NF ; next } # On START, use the last field as file name
/^END/ { next } # On END, skip
{ print > filename } # For the rest of the lines, print to file
Assume your data file is called data.txt, the following will do what you want:
awk -f print_between_patterns.awk data.txt
Discussion
After the script ran, you will have ./body1, ./body2, and so on.
If you don't want to skip the BEGIN and END parts, remove the next commands.
Update
If you want to control the output filename in a sequential way:
/^START/ { filename = sprintf("out%04d.txt", ++count) ; next }
/^END/ { next }
{ print > filename }
To get automatically generated incremental file names:
awk '
/^END/ { inBlock=0 }
inBlock { print > outfile }
/^START/ { inBlock=1; outfile = "outfile" ++count }
' file
To use the file names from your input:
awk '
/^END/ { inBlock=0 }
inBlock { print > outfile }
/^START/ {
inBlock=1
outdir = outfile = $NF
sub(/\/[^\/]+$/,"",outdir)
system("mkdir -p \"" outdir "\"")
}
' file
The problem #JamesBond was having below was that I wasn't escaping the "/" within the character list in the sub() so I've updated my answer above to do that now. There's absolutely no reason why that should need to be escaped but apparently both nawk and /usr/xpg4/bin/awk require it:
$ cat file
the
quick/brown
dog
$ gawk '/[/]/' file
quick/brown
$ nawk '/[/]/' file
nawk: nonterminated character class [
source line number 1
context is
>>> /[/ <<< ]/
$ /usr/xpg4/bin/awk '/[/]/' file
/usr/xpg4/bin/awk: /[/: [ ] imbalance or syntax error Context is:
>>> /[/ <<<
and gawk doesn't care either way:
$ gawk --lint --posix '/[/]/' file
quick/brown
$ gawk --lint '/[/]/' file
quick/brown
$ gawk --lint --posix '/[\/]/' file
quick/brown
$ gawk --lint '/[\/]/' file
quick/brown
They all work just fine if I escape the backslash without putting it in a character list:
$ /usr/xpg4/bin/awk '/\//' file
quick/brown
$ nawk '/\//' file
quick/brown
$ gawk '/\//' file
quick/brown
So I guess that's something worth remembering for portability in future!
Using awk:
awk 'sub(/^START/, ""){out=sprintf("out%d", c++); p=1}
sub(/^END/, ""){print > out; p=0} p{print > out}' file
This will find and store each match between START and END into separate files named out1, out2 etc.
This is one way to do it in Bash.
#!/bin/bash
[ -n "$BASH_VERSION" ] || {
echo "You need Bash to run this script."
exit 1
}
shopt -s extglob || {
echo "Unable to enable extglob shell option."
exit 1
}
IFS=$' \t\n' ## Use default.
while read KEY DASH FILENAME; do
if [[ $KEY == START && $DASH == --- && -n $FILENAME ]]; then
CURRENT_FILENAME=$FILENAME
DIRNAME=${FILENAME%%+([^/])}
if [[ -n $DIRNAME ]]; then
mkdir -p "$DIRNAME" || {
echo "Unable to create directory $DIRNAME."
exit 1
}
fi
exec 4>"$CURRENT_FILENAME" || {
echo "Unable to open $CURRENT_FILENAME for output."
exit 1
}
for (( ;; )); do
IFS= read -r LINE || {
echo "End of file reached finding END block of $CURRENT_FILENAME."
exec 4>&-
exit 1
}
read -r KEY DASH FILENAME <<< "$LINE"
if [[ $KEY == END && $DASH == --- && $FILENAME == "$CURRENT_FILENAME" ]]; then
break
else
echo "$LINE" >&4
fi
done
exec 4>&-
fi
done
Make sure you save the script in UNIX file format then run it as bash script.sh < file.
I guess you need to see this.
perl -lne 'print if((/START/../END/) and ($_!~/START/ and $_!~/END/))' your_file
Tested below:
> cat temp
START --- ./body1
##########################
123body1
abcbody1
##########################
END --- ./body1
START --- ./body2
##########################
123body2
defbody2
##########################
END --- ./body2
> perl -lne 'print if((/START/../END/) and ($_!~/START/ and $_!~/END/))' temp
##########################
123body1
abcbody1
##########################
##########################
123body2
defbody2
##########################
>
This might work for you:
csplit -z file '/^START/' '{*}'
Files will be named xx00 xx01 xx..

delete lines from multiple files using gawk / awk / sed

I have two sets of text files. First set is in AA folder. Second set is in BB folder. The content of ff.txt file from first set(AA folder) is shown below.
Name number marks
john 1 60
maria 2 54
samuel 3 62
ben 4 63
I would like to print the second column(number) from this file if marks>60. The output would be 3,4. Next, read the ff.txt file in the BB folder and delete the lines containing numbers 3,4.
files in the BB folder looks like this. second column is the number.
marks 1 11.824 24.015 41.220 1.00 13.65
marks 1 13.058 24.521 40.718 1.00 11.82
marks 3 12.120 13.472 46.317 1.00 10.62
marks 4 10.343 24.731 47.771 1.00 8.18
I used the following code.This code is working perfectly for one file.
gawk 'BEGIN {getline} $3>60{print $2}' AA/ff.txt | while read number; do gawk -v number=$number '$2 != number' BB/ff.txt > /tmp/ff.txt; mv /tmp/ff.txt BB/ff.txt; done
But when I run this code with multiple files, I get error.
gawk 'BEGIN {getline} $3>60{print $2}' AA/*.txt | while read number; do gawk -v number=$number '$2 != number' BB/*.txt > /tmp/*.txt; mv /tmp/*.txt BB/*.txt; done
error:-
mv: target `BB/kk.txt' is not a directory
I had asked this question two days ago.Please help me to solve this error.
This creates an index of all files in folder AA and checks against all files in folder BB:
cat AA/*.txt | awk 'FNR==NR { if ($3 > 60) array[$2]; next } !($2 in array)' - BB/*.txt
This compares two individual files, assuming they have the same name in folders AA and BB:
ls AA/*.txt | sed "s%AA/\(.*\)%awk 'FNR==NR { if (\$3 > 60) array[\$2]; next } !(\$2 in array)' & BB/\1 %" | sh
HTH
EDIT
This should help :-)
ls AA/*.txt | sed "s%AA/\(.*\)%awk 'FNR==NR { if (\$3 > 60) array[\$2]; next } !(\$2 in array)' & BB/\1 > \1_tmp \&\& mv \1_tmp BB/\1 %" | sh
> /tmp/*.txt and mv /tmp/*.txt BB/*.txt are wrong.
For single file
awk 'NR>1 && $3>60{print $2}' AA/ff.txt > idx.txt
awk 'NR==FNR{a[$0]; next}; !($2 in a)' idx.txt BB/ff.txt
For multiple files
awk 'FNR>1 && $3>60{print $2}' AA/*.txt >idx.txt
cat BB/*.txt | awk 'NR==FNR{a[$0]; next}; !($2 in a)' idx.txt -
One perl solution:
use warnings;
use strict;
use File::Spec;
## Hash to save data to delete from files of BB folder.
## key -> file name.
## value -> string with numbers of second column. They will be
## joined separated with '-...-', like: -2--3--1-. And it will be easier to
## search for them using a regexp.
my %delete;
## Check arguments:
## 1.- They are two.
## 2.- Both are directories.
## 3.- Both have same number of regular files and with identical names.
die qq[Usage: perl $0 <dir_AA> <dir_BB>\n] if
#ARGV != 2 ||
grep { ! -d } #ARGV;
{
my %h;
for ( glob join q[ ], map { qq[$_/*] } #ARGV ) {
next unless -f;
my $file = ( File::Spec->splitpath( $_ ) )[2] or next;
$h{ $file }++;
}
for ( values %h ) {
if ( $_ != 2 ) {
die qq[Different files in both directories\n];
}
}
}
## Get files from dir 'AA'. Process them, print to output lines which
## matches condition and save the information in the %delete hash.
for my $file ( glob( shift . qq[/*] ) ) {
open my $fh, q[<], $file or do { warn qq[Couldn't open file $file\n]; next };
$file = ( File::Spec->splitpath( $file ) )[2] or do {
warn qq[Couldn't get file name from path\n]; next };
while ( <$fh> ) {
next if $. == 1;
chomp;
my #f = split;
next unless #f >= 3;
if ( $f[ $#f ] > 60 ) {
$delete{ $file } .= qq/-$f[1]-/;
printf qq[%s\n], $_;
}
}
}
## Process files found in dir 'BB'. For each line, print it if not found in
## file from dir 'AA'.
{
#ARGV = glob( shift . qq[/*] );
$^I = q[.bak];
while ( <> ) {
## Sanity check. Shouldn't occur.
my $filename = ( File::Spec->splitpath( $ARGV ) )[2];
if ( ! exists $delete{ $filename } ) {
close ARGV;
next;
}
chomp;
my #f = split;
if ( $delete{ $filename } =~ m/-$f[1]-/ ) {
next;
}
printf qq[%s\n], $_;
}
}
exit 0;
A test:
Assuming next tree of files. Command:
ls -R1
Output:
.:
AA
BB
script.pl
./AA:
ff.txt
gg.txt
./BB:
ff.txt
gg.txt
And next content of files. Command:
head AA/*
Output:
==> AA/ff.txt <==
Name number marks
john 1 60
maria 2 54
samuel 3 62
ben 4 63
==> AA/gg.txt <==
Name number marks
john 1 70
maria 2 54
samuel 3 42
ben 4 33
Command:
head BB/*
Output:
==> BB/ff.txt <==
marks 1 11.824 24.015 41.220 1.00 13.65
marks 1 13.058 24.521 40.718 1.00 11.82
marks 3 12.120 13.472 46.317 1.00 10.62
marks 4 10.343 24.731 47.771 1.00 8.18
==> BB/gg.txt <==
marks 1 11.824 24.015 41.220 1.00 13.65
marks 2 13.058 24.521 40.718 1.00 11.82
marks 3 12.120 13.472 46.317 1.00 10.62
marks 4 10.343 24.731 47.771 1.00 8.18
Run the script like:
perl script.pl AA/ BB
With following ouput to screen:
samuel 3 62
ben 4 63
john 1 70
And files of BB directory modified like:
head BB/*
Output:
==> BB/ff.txt <==
marks 1 11.824 24.015 41.220 1.00 13.65
marks 1 13.058 24.521 40.718 1.00 11.82
==> BB/gg.txt <==
marks 2 13.058 24.521 40.718 1.00 11.82
marks 3 12.120 13.472 46.317 1.00 10.62
marks 4 10.343 24.731 47.771 1.00 8.18
So, from ff.txt lines with numbers 3 and 4 have been deleted, and lines with number 1 in gg.txt, which all of them were bigger than 60 in last column. I think this is what you wanted to achieve. I hope it helps, although not awk.

Splitting file based on variable

I have a file with several lines of the following:
DELIMITER ;
I want to create a separate file for each of these sections.
The man page of split command does not seem to have such option.
The split command only splits a file into blocks of equal size (maybe except for the last one).
However, awk is perfect for your type of problem. Here's a solution example.
Sample input
1
2
3
DELIMITER ;
4
5
6
7
DELIMITER ;
8
9
10
11
awk script split.awk
#!/usr/bin/awk -f
BEGIN {
n = 1;
outfile = n;
}
{
# FILENAME is undefined inside the BEGIN block
if (outfile == n) {
outfile = FILENAME n;
}
if ($0 ~ /DELIMITER ;/) {
n++;
outfile = FILENAME n;
} else {
print $0 >> outfile;
}
}
As pointed out by glenn jackman, the code also can be written as:
#!/usr/bin/awk -f
BEGIN {
n = 1;
}
$0 ~ /DELIMITER ;/ {
n++;
next;
}
{
print $0 >> FILENAME n;
}
The notation on the command prompt awk -v x="DELIMITER ;" -v n=1 '$0 ~ x {n++; next} {print > FILENAME n}' is more suitable if you don't use the script more often, however you can also save it in a file as well.
Test run
$ ls input*
input
$ chmod +x split.awk
$ ./split.awk input
$ ls input*
input input1 input2 input3
$ cat input1
1
2
3
$ cat input2
4
5
6
7
$ cat input3
8
9
10
11
The script is just a starting point. You probably have to adapt it to your personal needs and environment.