Estou tentando descobrir uma solução paraessepergunta. Minha abordagem para esse problema até agora é a seguinte.
- Anexe todos os caracteres para formar uma string longa.
- Após a etapa acima, remova todos os espaços em branco ou tabulações para que tenhamos apenas uma string grande.
Consegui estabelecer as etapas acima com o comando abaixo.
column -s '\t' inputfile | tr -d '[:space:]'
Então, para um arquivo de entrada como este,
1 0 0 0 0 0
0 1 1 1 0 0
Depois de aplicar o comando acima, tenho os valores como,
100000011100
Agora, nesta grande string, estou tentando aplicar uma abordagem conforme abaixo.
Extraia cada sexto caractere (conforme desejado pelo OP original) e anexe-o a um elemento da matriz até o final da string.
Então, basicamente, com a etapa acima, estou tentando criar os elementos do array como,
10
(1º e 7º caracteres ), 01
(2º e 8º caracteres ), 01
(3º e 9º caracteres ), 01
(4º e 10º caracteres ), 00
(5º e 11º caracteres ), 00
(6º e 12º caracteres). o personagem).
Então, minha pergunta é: como eu poderia extrair cada enésimo caractere para poder adicioná-los a uma matriz para prosseguir? (n=6, neste caso).
Responder1
Duas linhas
Aqui está uma bash
solução pura que produz um bash
array:
s="100000011100"
array=($(
for ((i=0; i<${#s}-6; i++))
do
echo "${s:$i:1}${s:$((i+6)):1}"
done
))
echo "${array[@]}"
Isso produz a mesma saída mostrada na pergunta:
10 01 01 01 00 00
O elemento chave aqui é o uso do bashexpansão de substring. Bash permite a extração de substrings de uma variável, digamos parameter
, via ${parameter:offset:length}
. No nosso caso, o deslocamento é determinado pela variável do loop i
e o comprimento é sempre 1
.
Solução geral para qualquer número de linhas
Suponha, por exemplo, que nossa string original tenha 18 caracteres e queremos extrair o i-ésimo, o i+6-ésimo e o i+12-ésimo caracteres para i de 0 a 5. Então:
s="100000011100234567"
array=($(
for ((i=0; i<6; i++))
do
new=${s:$i:1}
for ((j=i+6; j<${#s}; j=j+6))
do
new="$new${s:$j:1}"
done
echo "$new"
done
))
echo "${array[@]}"
Isso produz a saída:
102 013 014 015 006 007
Este mesmo código se estende a um número arbitrário de linhas de 6 caracteres. Por exemplo, se s
tiver três linhas (18 caracteres):
s="100000011100234567abcdef"
Então, a saída se torna:
102a 013b 014c 015d 006e 007f
Responder2
Usando perl
:
$ echo 100000011100 | perl -nle '
for ($i = 0; $i < length()/2; $i++) {
print substr($_,$i,1), substr($_,$i+6,1);
}
'
10
01
01
01
00
00
Funciona para duas linhas. Se você quiser trabalhar com linhas arbitrárias, você deve processar as linhas diretamente, em vez de construir uma string grande. Com esta entrada:
1 0 0 0 0 0
0 1 1 1 0 0
0 0 0 0 0 0
Tentar:
$ perl -anle '
for ($i = 0; $i <= $#F; $i++) {
push @{$h{$i}}, $F[$i];
}
END {
print @{$h{$_}} for keys %h;
}
' file
000
010
000
100
010
010
Responder3
Como solução shell, getopts
é provavelmente mais fácil. O problema getopts
é que ele é especificado pelo POSIX para fazer exatamente o que você está pedindo - processar um fluxo de bytes em um loop de shell. Eu sei que isso parece estranho, porque, se você é como eu antes de aprender isso, provavelmente está pensando:bem, caramba, pensei que era para lidar com opções de linha de comando.O que é verdade, mas a primeira coisa também é. Considerar:
-thisisonelongstringconsistingofseparatecommandlineswitches
Sim, getopts
tem que lidar com isso. Ele tem que dividir char por char em um loop e retornar para você cada caractere na variável shell $OPTARG
ou em outra que você especifica por nome, tudo dependendo de quão específico você obtém ao chamá-lo. Além do mais, ele deve retornar erros nas variáveis do shell esalve seu progressoquando isso acontece na variável shell $OPTIND
para que possaretomar exatamente de onde parouse você puder de alguma forma resolver isso. E tem que fazer todo o trabalho sem invocar um único subshell.
Então digamos que temos:
arg=$(seq -s '' 1000); set --
while getopts :0123456789 v -"${arg}"
do [ "$((i=$i+1<6?$i+1:0))" -gt 0 ] ||
set "$@" "$v"
done
Hmmm.... Será que funcionou?
echo "$((${#arg}/6))" "$#"
482 482
Muito legal...
eval '
printf %.1s\\n "${arg#'"$(printf %0$((124*6-1))d | tr 0 \?)"'}" "${124}"'
4
4
Então, como você pode ver, o getopts
comando define completamente o array para cada sexto byte da string. E não precisa ser números como este - nem deve ser caracteres seguros para shell - e você nem precisa especificar os caracteres de destino como fiz acima 01234565789
. Eu testei isso repetidamente em vários shells e todos eles simplesmente funcionam. Existem algumas peculiaridades - bash
descartará o primeiro caractere se for um caractere de espaço em branco - dash
aceita os :
dois pontos como um parâmetro especificado, mesmo que seja o único que o POSIX proíbe especificamente. Mas nada disso importa porque getopts
ainda deposita o valor do opt char atual $OPTARG
mesmo quando ele retorna um erro(representado por um ? atribuído à sua opt var especificada)e de outra forma explicitamente desativada, $OPTARG
a menos que você tenha declarado que uma opção deve ter um argumento. E a coisa do espaço em branco é uma coisa boa - apenas descarta umprincipalespaço, o que é excelente, pois, ao trabalhar com valores desconhecidos, você pode fazer:
getopts : o -" $unknown_value"
... para iniciar o loop sem qualquer perigo de o primeiro caractere realmente estar na sua string de argumentos aceita - o que resultaria em getopts
inserir tudo $OPTARG
de uma vez - como um argumento.
Aqui está outro exemplo:
OPTIND=1
while getopts : o -" $(dd if=/dev/urandom bs=16 count=1 2>/dev/null)"
do printf '\\%04o' "'$OPTARG"; done
\0040\0150\0071\0365\0320\0070\0161\0064\0274\0115\0012\0215\0222\0271\0146\0057\0166
Eu configurei $OPTIND=1
na primeira linha porque acabei de usar getopts
e, até que você reinicie, ele espera que sua próxima chamada continue de onde parou - "${arg2}"
em outras palavras, ele quer. Mas não estou com vontade de dar e estou fazendo uma coisa diferente agora, então eu informo redefinindo $OPTIND
em que ponto está pronto para ir.
Neste eu usei zsh
- o que não questiona o espaço inicial - e então o primeiro caractere é octal 40 - o caractere de espaço. Mas eu não costumo usar getopts
dessa maneira - geralmente uso paraevitarfazendo um write()
para cada byte e, em vez disso, atribua sua saída - que vem em uma variável - a outra variável de shell - como fiz acima de set
certa forma. Então, quando estiver pronto, posso pegar a string inteira e, quando o faço, geralmente removo o primeiro byte.
Responder4
sed
é a primeira coisa que me vem à mente.
$ echo 1234567890abcdefghijklmnopqrstuvwxyz | sed 's/.\{5\}\(.\)/\1/g'
6bhntz
Combine 5 personagens, capture o 6º e substitua todos pelo personagem capturado.
No entanto, isso terá um problema se o comprimento da string não for um múltiplo exato de 6:
$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{5\}\(.\)/\1/g'
6bhntuvwxy
Mas podemos consertar isso alterando sed
um pouco:
$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{1,5\}\(.\{0,1\}\)/\1/g'
6bhnt
Devido à natureza gananciosa do regex, as correspondências de comprimento variável corresponderão o máximo que puderem e, se não sobrar nada para a captura, ele não será capturado e os caracteres serão simplesmente excluídos.