2012-08-13 14 views
6

Puedo hacer que esto funcione en ksh pero no en bash, lo que realmente me está volviendo loco. Afortunadamente es algo obvio que estoy pasando por alto.bash problema al asignar a un índice de matriz en un bucle

Necesito ejecutar un comando externo donde cada línea de la salida se almacenará en un índice de matriz.

Este ejemplo simplificado parece que está configurando la matriz en el ciclo correctamente; sin embargo, después de que el ciclo haya completado esas asignaciones de matriz se han ido? ¿Es como si el lazo fuera tratado completamente como un caparazón externo?

junk.txt

this is a 
test to see 
if this works ok 

testa.sh

#!/bin/bash 

declare -i i=0 
declare -a array 

echo "Simple Test:" 
array[0]="hello" 
echo "array[0] = ${array[0]}" 

echo -e "\nLoop through junk.txt:" 
cat junk.txt | while read line 
do 
    array[i]="$line" 
    echo "array[$i] = ${array[i]}" 
    let i++ 
done 

echo -e "\nResults:" 
echo "  array[0] = ${array[0]}" 
echo " Total in array = ${#array[*]}" 
echo "The whole array:" 
echo ${array[@]} 

salida

Simple Test: 
array[0] = hello 

Loop through junk.txt: 
array[0] = this is a 
array[1] = test to see 
array[2] = if this works ok 

Results: 
     array[0] = hello 
Total in array = 1 
The whole array: 
hello 

Así, mientras que en el bucle, asignamos array [i] y el eco lo verifica. Pero después del ciclo volví a la matriz [0] que contenía "hola" sin ningún otro elemento.

Los mismos resultados en bash 3, 4 y diferentes plataformas.

Respuesta

7

Como su ciclo while está en una canalización, todas las asignaciones de variables en el cuerpo del bucle son locales para la subshell en la que se ejecuta el bucle. (Creo ksh no se ejecuta el comando en un subnivel, razón por la cual usted tiene el problema en bash.) Haga esto en su lugar:

while read line 
do 
    array[i]="$line" 
    echo "array[$i] = ${array[i]}" 
    let i++ 
done < junk.txt 

Rara vez, o nunca, ¿desea utilizar cat a la tubería de un solo archivo a otro comando; use la redirección de entrada en su lugar.

ACTUALIZACIÓN: ya que se necesita para funcionar de una orden y no un archivo, otra opción (si está disponible) es la sustitución de proceso:

while read line; do 
... 
done < <(command args ...) 

Si la sustitución de proceso no está disponible, que necesita para la producción de un archivo temporal y la entrada de redirección de ese archivo.

Si se está utilizando bash 4.2 o posterior, puede ejecutar estos dos comandos antes de su bucle, y el tubo-en-the-loop original funciona, ya que el bucle while es la última comando en la canalización.

set +m # Turn off job control; it's probably already off in a non-interactive script 
shopt -s lastpipe 
cat junk.txt | while read line; do ...; done 

ACTUALIZACIÓN 2: Aquí es una solución bucle menos, basado en el comentario de user1596414

array[0]=hello 
IFS=$'\n' array+=($(command)) 

la salida de su orden se divide en palabras basadas únicamente en los saltos de línea (de forma que cada línea es una palabra separada), y agrega la matriz de línea por ranura resultante al original. Esto es muy bueno si solo está utilizando el bucle para construir la matriz. También puede modificarse para acomodar una pequeña cantidad de procesamiento por línea, vagamente similar a una comprensión de la lista de Python.

+0

"Necesito ejecutar un comando externo donde cada línea de la salida se almacenará en un índice de matriz." El "gato" fue un ejemplo simplificado. Necesito ejecutar un comando y obtener su salida en el bucle y una redirección como user1596414

+0

Actualizado con algunas opciones. El último (si tu versión de bash es lo suficientemente nueva) es probablemente la que quieres. – chepner

+1

+1 funciona un archivo temporal o bash 4.2 (o posterior). Hay una tercera opción que usa IFS para manejar espacios en blanco y se puede combinar con una asignación de matriz simple si no hay necesidad de iterar a través de la salida también. – user1596414

Cuestiones relacionadas