O script de shell a seguir usa uma opção opcional -d
para definir o delimitador (a guia é padrão), bem como uma opção -c
não opcional com uma especificação de coluna.
A especificação da coluna é semelhante à de cut
, mas também permite reorganizar e duplicar as colunas de saída, além de especificar as faixas ao contrário. Os intervalos abertos também são suportados.
O arquivo a ser analisado é dado na linha de comando como o último operando ou passado na entrada padrão.
#!/bin/sh
delim='\t' # tab is default delimiter
# parse command line option
while getopts 'd:c:' opt; do
case $opt in
d)
delim=$OPTARG
;;
c)
cols=$OPTARG
;;
*)
echo 'Error in command line parsing' >&2
exit 1
esac
done
shift "$(( OPTIND - 1 ))"
if [ -z "$cols" ]; then
echo 'Missing column specification (the -c option)' >&2
exit 1
fi
# ${1:--} will expand to the filename or to "-" if $1 is empty or unset
cat "${1:--}" |
awk -F "$delim" -v cols="$cols" '
BEGIN {
# output delim will be same as input delim
OFS = FS
# get array of column specs
ncolspec = split(cols, colspec, ",")
}
{
# get fields of current line
# (need this as we are rewriting $0 below)
split($0, fields, FS)
nf = NF # save NF in case we have an open-ended range
$0 = ""; # empty $0
# go through given column specification and
# create a record from it
for (i = 1; i <= ncolspec; ++i)
if (split(colspec[i], r, "-") == 1)
# single column spec
$(NF+1) = fields[colspec[i]]
else {
# column range spec
if (r[1] == "") r[1] = 1 # open start range
if (r[2] == "") r[2] = nf # open end range
if (r[1] < r[2])
# forward range
for (j = r[1]; j <= r[2]; ++j)
$(NF + 1) = fields[j]
else
# backward range
for (j = r[1]; j >= r[2]; --j)
$(NF + 1) = fields[j]
}
print
}'
Há uma pequena ineficiência nisso, pois o código precisa analisar novamente a especificação da coluna para cada nova linha. Se o suporte para intervalos abertos não for necessário, ou se todas as linhas tiverem exatamente o mesmo número de colunas, somente uma única passagem sobre a especificação poderá ser feita no bloco BEGIN
(ou em uma separação NR==1
block) para criar uma matriz de campos que devem ser gerados.
Ausente: verificação de integridade para a especificação da coluna. Uma sequência de especificação malformada pode causar estranheza.
Teste:
$ cat file
1:2:3
a:b:c
@:(:)
$ sh script.sh -d : -c 1,3 <file
1:3
a:c
@:)
$ sh script.sh -d : -c 3,1 <file
3:1
c:a
):@
$ sh script.sh -d : -c 3-1,1,1-3 <file
3:2:1:1:1:2:3
c:b:a:a:a:b:c
):(:@:@:@:(:)
$ sh script.sh -d : -c 1-,3 <file
1:2:3:3
a:b:c:c
@:(:):)