Estoy tratando de encontrar una solución paraestepregunta. Mi enfoque a este problema hasta ahora es el siguiente.
- Agregue todos los caracteres juntos para formar una cadena larga.
- Después del paso anterior, elimine todos los espacios en blanco o espacios de tabulación para que solo tengamos una cadena grande.
Pude establecer los pasos anteriores con el siguiente comando.
column -s '\t' inputfile | tr -d '[:space:]'
Entonces, para un archivo de entrada como este,
1 0 0 0 0 0
0 1 1 1 0 0
Después de aplicar el comando anterior tengo los valores como,
100000011100
Ahora, en esta gran cadena, estoy intentando aplicar el enfoque que se muestra a continuación.
Extraiga cada sexto carácter (como quiere el OP original) y agréguelo a un elemento de matriz hasta el final de la cadena.
Básicamente, con el paso anterior, estoy tratando de crear los elementos de la matriz como,
10
(1.º y 7.º carácter ), 01
(2.º y 8.º carácter ), 01
(3.º y 9.º carácter ), 01
(4.º y 10.º carácter ), 00
( 5.º y 11.º carácter ), 00
(6.º y 12.º carácter), º personaje) .
Entonces mi pregunta es, ¿cómo podría extraer cada enésimo carácter para poder agregarlos a una matriz y continuar? (n=6, en este caso).
Respuesta1
Dos lineas
Aquí hay una bash
solución pura que produce una bash
matriz:
s="100000011100"
array=($(
for ((i=0; i<${#s}-6; i++))
do
echo "${s:$i:1}${s:$((i+6)):1}"
done
))
echo "${array[@]}"
Esto produce el mismo resultado que se muestra en la pregunta:
10 01 01 01 00 00
El elemento clave aquí es el uso de bash.expansión de subcadena. Bash permite extraer subcadenas de una variable, por ejemplo parameter
, a través de ${parameter:offset:length}
. En nuestro caso, el desplazamiento está determinado por la variable del bucle i
y la longitud es siempre 1
.
Solución general para cualquier número de líneas
Supongamos, por ejemplo, que nuestra cadena original tiene 18 caracteres y queremos extraer los caracteres i-ésimo, i+6-ésimo y i+12-ésimo para i de 0 a 5. Entonces:
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[@]}"
Esto produce la salida:
102 013 014 015 006 007
Este mismo código se extiende a un número arbitrario de líneas de 6 caracteres. Por ejemplo, si s
tiene tres líneas (18 caracteres):
s="100000011100234567abcdef"
Entonces, la salida se convierte en:
102a 013b 014c 015d 006e 007f
Respuesta2
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 dos líneas. Si desea trabajar con líneas arbitrarias, debe procesar las líneas directamente, en lugar de crear cadenas grandes. Con esta entrada:
1 0 0 0 0 0
0 1 1 1 0 0
0 0 0 0 0 0
Intentar:
$ 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
Respuesta3
Como solución shell, getopts
probablemente sea la más sencilla. Lo que pasa getopts
es que está especificado por POSIX para hacer exactamente lo que estás pidiendo: procesar un flujo de bytes en un bucle de shell. Sé que suena raro porque, si eres como yo antes de enterarme de esto, probablemente estés pensando:Bueno, vaya, pensé que se suponía que debía manejar cambios de línea de comandos.Lo cual es cierto, pero también lo es lo primero. Considerar:
-thisisonelongstringconsistingofseparatecommandlineswitches
Sí, getopts
tiene que manejar eso. Tiene que dividir ese carácter por carácter en un bucle y devolverle cada carácter en la variable de shell $OPTARG
o en otra que especifique por nombre, todo dependiendo de qué tan específico sea cuando lo llame. Es más, tiene que devolver errores en las variables del shell yguardar su progresocuando lo hace en la variable de shell $OPTIND
para que puedareanudar justo donde lo dejósi de alguna manera puedes solucionarlo. Y tiene que hacer todo el trabajo sin invocar una sola subcapa.
Entonces digamos que tenemos:
arg=$(seq -s '' 1000); set --
while getopts :0123456789 v -"${arg}"
do [ "$((i=$i+1<6?$i+1:0))" -gt 0 ] ||
set "$@" "$v"
done
Hmmm.... Me pregunto si funcionó.
echo "$((${#arg}/6))" "$#"
482 482
Qué lindo...
eval '
printf %.1s\\n "${arg#'"$(printf %0$((124*6-1))d | tr 0 \?)"'}" "${124}"'
4
4
Entonces, como puede ver, el getopts
comando configura completamente la matriz para cada sexto byte de la cadena. Y no tienen que ser números como este, ni siquiera deben ser caracteres seguros para el shell, y ni siquiera es necesario especificar los caracteres de destino como lo hice anteriormente con 01234565789
ninguno de los dos. He probado esto repetidamente en muchos shells y todos simplemente funcionan. Hay algunas peculiaridades: bash
descartará el primer carácter si es un espacio en blanco; dash
acepta los :
dos puntos como parámetro especificado a pesar de que es el único POSIX que lo prohíbe específicamente. Pero nada de eso importa porque getopts
aún deposita el valor del carácter de opción actual $OPTARG
incluso cuando le devuelve un error.(representado por un? asignado a su var de opción especificada)y de lo contrario se desarma explícitamente $OPTARG
a menos que haya declarado que una opción debería tener un argumento. Y lo de los espacios en blanco es algo bueno: sólo descarta unaprincipalespacio, lo cual es excelente, porque, cuando se trabaja con valores desconocidos, puedes hacer:
getopts : o -" $unknown_value"
... para iniciar el ciclo sin ningún peligro de que el primer carácter esté realmente en la cadena de argumentos aceptada, lo que daría como resultado getopts
incluir todo $OPTARG
a la vez, como argumento.
Aquí hay otro ejemplo:
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
Lo configuré $OPTIND=1
en la primera línea porque acabo de usar getopts
y, hasta que lo restablezca, espera que su próxima llamada continúe donde lo dejó; "${arg2}"
en otras palabras, quiere. Pero no tengo ganas de dar y ahora estoy haciendo algo diferente, así que le hago saber reiniciando $OPTIND
en qué punto está listo.
En este usé zsh
, que no tiene objeciones sobre un espacio inicial, por lo que el primer carácter es octal 40, el carácter de espacio. Aunque normalmente no lo uso getopts
de esa manera; normalmente lo uso paraevitarhaciendo a write()
para cada byte y en su lugar asigna su salida, que viene en una variable, a otra variable de shell, como lo hice anteriormente en set
cierto modo. Luego, cuando esté listo, puedo tomar toda la cadena y, cuando lo hago, suelo eliminar el primer byte.
Respuesta4
sed
es lo primero que me viene a la mente.
$ echo 1234567890abcdefghijklmnopqrstuvwxyz | sed 's/.\{5\}\(.\)/\1/g'
6bhntz
Combina 5 personajes, captura el sexto y reemplázalos todos con ese personaje capturado.
Sin embargo, esto tendrá un problema si la longitud de la cadena no es un múltiplo exacto de 6:
$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{5\}\(.\)/\1/g'
6bhntuvwxy
Pero podemos solucionar esto modificando un sed
poco:
$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{1,5\}\(.\{0,1\}\)/\1/g'
6bhnt
Debido a la naturaleza codiciosa de las expresiones regulares, las coincidencias de longitud variable coincidirán tanto como puedan, y si no queda nada para la captura, entonces no se captura y los caracteres simplemente se eliminan.