sed -e ':L' -e 's/\(\([^|]*|\)\{10\}X*\)[0-9]\([0-9]\{4\}\)/\1X\3/;tL' sample.txt
The \(\([^|]*|\)\{10\}X*\) matches the first 10 fields plus already substituted X es in field 11, and is put back as \1 .
The 4 digit look-ahead \([0-9]\{4\}\) is put back as \3 .
The one [0-9] is really substituted by the X .
The whole thing repeats in a loop, until nothing is left that can be substituted. (A /g option does not work because it cannot repeat on anything before the last matched character i.e. the look-ahead.)
A solution with awk or perl or bash would look simpler, because you can split on "|" and only work on field 11.
Depending on what the rest of the shell script looks like (and, most prominently, WHICH SHELL YOU ARE USING - you might have considered telling us) you can do it with variable expansion. Here is a solution in Korn shell, i haven't tested it in bash,but it should work there too (replace "print" with "echo" then):
#! /bin/ksh
function maskpart
{
mask="${1%????}" # extract everything save for the last 4 digits
mask="${mask//?/x}" # and replace all characters with x's
print - "${mask}${1#${1%????}}" # print the masked part and the last 4 digits
return 0
}
# here are examples of how to use the function:
maskpart "123456-7890"
maskpart "blafoo1234"
myvar="$(maskpart "123456-7890")" ; print - $myvar
while read BAN ; do
printf "$BAN \t==> " ; maskpart "$BAN"
done < /file/with/bank-account-numbers
exit 0
The function does not cover for bank-account-numbers being 4 digits or shorter (or otherwise malformed). If this could be the case you will have to provide extra logic.
Here comes a bash script, using a short version of bakunin's function and a print function. (The shell is lacking a builtin join function. Gives me the chance to demonstrate the power of a custom function.)
#!/bin/bash
sep="|"
maskpart(){
local mask="${1%????}"
echo "${mask//?/X}${1#$mask}"
}
joinprint(){
local a s=$1 out=$2
shift 2
for a
do
out=$out$s$a
done
echo "$out"
}
while IFS=$sep read -a arr
do
arr[10]=$(maskpart "${arr[10]}")
joinprint "$sep" "${arr[@]}"
done
Firstly, please start using code tags as outlined in the Forum's rules.
awk doesn't have the inline editing capabilities. You'll have wrap awk within a shell script, saving the output of awk to a temp file and then MVing the temp file to the original (when satisfied with the results).
E.g.