una recorrer matriz matrices hacer for eliminar elemento con como arreglos array arrays bash

arrays - recorrer - ¿Cómo puedo unir elementos de una matriz en Bash?



matrices bash shell (26)

Ahora mismo estoy usando:

TO_IGNORE=( E201 # Whitespace after ''('' E301 # Expected N blank lines, found M E303 # Too many blank lines (pep8 gets confused by comments) ) ARGS="--ignore `echo ${TO_IGNORE[@]} | tr '' '' '',''`"

Lo que funciona, pero (en el caso general) se romperá horriblemente si los elementos de la matriz tienen un espacio en ellos.

(Para aquellos interesados, este es un script envoltorio alrededor de pep8.py )

Si tengo una matriz como esta en Bash:

FOO=( a b c )

¿Cómo puedo unir los elementos con comas? Por ejemplo, produciendo a,b,c .


Aquí hay una función Bash 100% pura que hace el trabajo:

join() { # $1 is return variable name # $2 is sep # $3... are the elements to join local retname=$1 sep=$2 ret=$3 shift 3 || shift $(($#)) printf -v "$retname" "%s" "$ret${@/#/$sep}" }

Mira:

$ a=( one two "three three" four five ) $ join joineda " and " "${a[@]}" $ echo "$joineda" one and two and three three and four and five $ join joinedb randomsep "only one element" $ echo "$joinedb" only one element $ join joinedc randomsep $ echo "$joinedc" $ a=( $'' stuff with/nnewlines/n'' $''and trailing newlines/n/n'' ) $ join joineda $''a sep with/nnewlines/n'' "${a[@]}" $ echo "$joineda" stuff with newlines a sep with newlines and trailing newlines $

Esto conserva incluso las nuevas líneas finales, y no necesita una subshell para obtener el resultado de la función. Si no te gusta el printf -v (¿por qué no te gustaría?) Y pasar un nombre de variable, puedes usar una variable global para la cadena devuelta:

join() { # $1 is sep # $2... are the elements to join # return is in global variable join_ret local sep=$1 IFS= join_ret=$2 shift 2 || shift $(($#)) join_ret+="${*/#/$sep}" }


Combina lo mejor de todos los mundos hasta ahora con la siguiente idea.

# join with separator join_ws() { local IFS=; local s="${*/#/$1}"; echo "${s#"$1$1$1"}"; }

Esta pequeña obra maestra es

  • 100% puro bash (expansión de parámetros con IFS temporalmente desarmado, sin llamadas externas, sin impresión ...)
  • compacto, completo e impecable (funciona con limitadores de uno o varios caracteres, funciona con limitadores que contienen espacios en blanco, saltos de línea y otros caracteres especiales de concha, funciona con delimitador vacío)
  • eficiente (sin subshell, sin copia de matriz)
  • Simple y estúpido y, hasta cierto punto, hermoso e instructivo también.

Ejemplos:

$ join_ws , a b c a,b,c $ join_ws '''' a b c abc $ join_ws $''/n'' a b c a b c $ join_ws '' // '' A B C A // B // C


Con la reutilización de la solución @ no importa, pero con una sola declaración al evitar la subestación $ {: 1} y la necesidad de una variable intermedia.

echo $(printf "%s," "${LIST[@]}" | cut -d "," -f 1-${#LIST[@]} )

printf tiene ''La cadena de formato se reutiliza tantas veces como sea necesario para satisfacer los argumentos''. en sus páginas de manual, para documentar las concatenaciones de las cadenas. Luego, el truco es usar la longitud de la LISTA para cortar el último sperator, ya que el corte mantendrá solo la longitud de la LISTA cuando los campos cuenten.


En caso de que los elementos a los que desea unirse no sean una matriz, solo una cadena separada por espacios, puede hacer algo como esto:

foo="aa bb cc dd" bar=`for i in $foo; do printf ",''%s''" $i; done` bar=${bar:1} echo $bar ''aa'',''bb'',''cc'',''dd''

por ejemplo, mi caso de uso es que algunas cadenas se pasan en mi script de shell y necesito usar esto para ejecutar una consulta SQL:

./my_script "aa bb cc dd"

En my_script, necesito hacer "SELECT * FROM la tabla WHERE name IN (''aa'', ''bb'', ''cc'', ''dd''). El comando anterior será útil.


Este enfoque se ocupa de los espacios dentro de los valores, pero requiere un bucle:

#!/bin/bash FOO=( a b c ) BAR="" for index in ${!FOO[*]} do BAR="$BAR,${FOO[$index]}" done echo ${BAR:1}


Esto no es muy diferente de las soluciones existentes, pero evita el uso de una función separada, no modifica IFS en el shell primario y está todo en una sola línea:

arr=(a b c) printf ''%s/n'' "$(IFS=,; echo "${arr[*]}")"

Resultando en

a,b,c


Gracias a @gniourf_gniourf por los comentarios detallados sobre mi combinación de los mejores mundos hasta ahora. Lo siento por el código de publicación no completamente diseñado y probado. Aquí hay un mejor intento.

# join with separator join_ws() { local d=$1 s=$2; shift 2 && printf %s "$s${@/#/$d}"; }

Esta belleza por concepción es.

  • (aún) 100% de bash puro (gracias por señalar explícitamente que printf también está integrado. No sabía esto antes ...)
  • Funciona con delimitadores de múltiples caracteres.
  • más compacto y más completo, y esta vez, cuidadosamente analizado y a largo plazo, sometido a prueba con subcadenas aleatorias de scripts de shell, entre otros, que cubren el uso de caracteres especiales de shell o caracteres de control o ningún carácter tanto en el separador como en los parámetros, y en casos extremos , y los casos de esquina y otras cuestiones como ningún argumento en absoluto. Eso no garantiza que no haya más errores, pero será un desafío un poco más difícil encontrar uno. Por cierto, incluso las respuestas más votadas actualmente y relacionadas con este tipo de cosas como ese -e error ...

Ejemplos adicionales:

$ join_ws '''' a b c abc $ join_ws '':'' {1,7}{A..C} 1A:1B:1C:7A:7B:7C $ join_ws -e -e -e $ join_ws $''/033[F'' $''/n/n/n'' 1. 2. 3. $''/n/n/n/n'' 3. 2. 1. $ join_ws $ $


Mi intento.

$ array=(one two "three four" five) $ echo "${array[0]}$(printf " SEP %s" "${array[@]:1}")" one SEP two SEP three four SEP five


Otra solución más:

#!/bin/bash foo=(''foo bar'' ''foo baz'' ''bar baz'') bar=$(printf ",%s" "${foo[@]}") bar=${bar:1} echo $bar

Edición: igual pero para separador de longitud variable de múltiples caracteres:

#!/bin/bash separator=")|(" # e.g. constructing regex, pray it does not contain %s foo=(''foo bar'' ''foo baz'' ''bar baz'') regex="$( printf "${separator}%s" "${foo[@]}" )" regex="${regex:${#separator}}" # remove leading separator echo "${regex}" # Prints: foo bar)|(foo baz)|(bar baz


Repetiría la matriz como una cadena, luego transformaré los espacios en líneas y luego paste para unir todo en una línea, así:

tr " " "/n" <<< "$FOO" | paste -sd , -

Resultados:

a,b,c

¡Este parece ser el más rápido y limpio para mí!


Si construyes la matriz en un bucle, aquí hay una manera simple:

arr=() for x in $(some_cmd); do arr+=($x,) done arr[-1]=${arr[-1]%,} echo ${arr[*]}


Sin usar comandos externos:

$ FOO=( a b c ) # initialize the array $ BAR=${FOO[@]} # create a space delimited string from array $ BAZ=${BAR// /,} # use parameter expansion to substitute spaces with comma $ echo $BAZ a,b,c

Advertencia, se supone que los elementos no tienen espacios en blanco.


Solución de printf que acepta separadores de cualquier longitud (basado en @ no importa la respuesta)

#/!bin/bash foo=(''foo bar'' ''foo baz'' ''bar baz'') sep='','' # can be of any length bar=$(printf "${sep}%s" "${foo[@]}") bar=${bar:${#sep}} echo $bar


Solución de reescritura de Pascal Pilz como una función en Bash 100% puro (sin comandos externos):

function join_by { local IFS="$1"; shift; echo "$*"; }

Por ejemplo,

join_by , a "b c" d #a,b c,d join_by / var local tmp #var/local/tmp join_by , "${FOO[@]}" #a,b,c

Alternativamente, podemos usar printf para admitir delimitadores de múltiples caracteres, usando la idea de @gniourf_gniourf

function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; }

Por ejemplo,

join_by , a b c #a,b,c join_by '' , '' a b c #a , b , c join_by '')|('' a b c #a)|(b)|(c join_by '' %s '' a b c #a %s b %s c join_by $''/n'' a b c #a<newline>b<newline>c join_by - a b c #a-b-c join_by ''/' a b c #a/b/c


Sorprendentemente, mi solución aún no está dada :) Esta es la forma más simple para mí. No necesita una función:

IFS=, eval ''joined="${foo[*]}"''

Nota: Se observó que esta solución funciona bien en modo no POSIX. En el modo POSIX , los elementos aún se unen correctamente, pero IFS=, vuelve permanente.


Tal vez me esté perdiendo algo obvio, ya que soy nuevo en todo lo relacionado con bash / zsh, pero me parece que no necesitas usar printf en absoluto. Tampoco se vuelve realmente feo prescindir de ello.

join() { separator=$1 arr=$* arr=${arr:2} # throw away separator and following space arr=${arr// /$separator} }

Al menos, ha funcionado para mí hasta ahora sin problema.

Por ejemplo, join /| *.sh join /| *.sh , que, digamos que estoy en mi ~ directorio, genera utilities.sh|play.sh|foobar.sh . Suficientemente bueno para mi.

EDIT: Esta es básicamente la respuesta de Nil Geisweiller , pero generalizada en una función.


Tal vez, por ejemplo,

SAVE_IFS="$IFS" IFS="," FOOJOIN="${FOO[*]}" IFS="$SAVE_IFS" echo "$FOOJOIN"


Usar el direccionamiento de variables para referirse directamente a una matriz también funciona. Las referencias con nombre también se pueden usar, pero solo estuvieron disponibles en 4.3.

La ventaja de usar esta forma de una función es que puede tener el separador opcional (el valor predeterminado es el primer carácter de IFS predeterminado, que es un espacio; tal vez haga que sea una cadena vacía si lo desea), y evita expandir los valores dos veces ( primero cuando se pasa como parámetros, y segundo como "$@" dentro de la función).

Esta solución tampoco requiere que el usuario llame a la función dentro de una sustitución de comando, que convoca una subshell, para obtener una versión unida de una cadena asignada a otra variable.

En cuanto a las desventajas: tendría que tener cuidado al pasar el nombre correcto del parámetro, y al pasar __r le daría __r[@] . El comportamiento de la dirección indirecta variable para expandir también otras formas de parámetros tampoco está documentado explícitamente.

function join_by_ref { __= local __r=$1[@] __s=${2-'' ''} printf -v __ "%s${__s///%/%%}" "${!__r}" __=${__%${__s}} } array=(1 2 3 4) join_by_ref array echo "$__" # Prints ''1 2 3 4''. join_by_ref array ''%s'' echo "$__" # Prints ''1%s2%s3%s4''. join_by_ref ''invalid*'' ''%s'' # Bash 4.4 shows "invalid*[@]: bad substitution". echo "$__" # Prints nothing but newline.

Esto funciona de 3.1 a 5.0-alpha. Como se observó, la indirección de variables no solo funciona con variables, sino también con otros parámetros.

Un parámetro es una entidad que almacena valores. Puede ser un nombre, un número o uno de los caracteres especiales que se enumeran a continuación en Parámetros especiales. Una variable es un parámetro denotado por un nombre.

Las matrices y los elementos de la matriz también son parámetros (entidades que almacenan valor), y las referencias a las matrices también son referencias técnicas a los parámetros. Y al igual que el parámetro especial @ , array[@] también es una referencia válida.

Las formas de expansión alteradas o selectivas (como la expansión de subcadenas) que desvían la referencia del parámetro en sí ya no funcionan.


Utilice perl para separadores de caracteres múltiples:

function join { perl -e ''$s = shift @ARGV; print join($s, @ARGV);'' "$@"; } join '', '' a b c # a, b, c

O en una línea:

perl -le ''print join(shift, @ARGV);'' '', '' 1 2 3 1, 2, 3


Versión más corta de la mejor respuesta:

joinStrings() { local a=("${@:3}"); printf "%s" "$2${a[@]/#/$1}"; }

Uso:

joinStrings "$myDelimiter" "${myArray[@]}"


$ foo=(a "b c" d) $ bar=$(IFS=, ; echo "${foo[*]}") $ echo "$bar" a,b c,d


$ set a ''b c'' d $ history -p "$@" | paste -sd, a,b c,d


awk -v sep=. ''BEGIN{ORS=OFS="";for(i=1;i<ARGC;i++){print ARGV[i],ARGC-i-1?sep:""}}'' "${arr[@]}"

o

$ a=(1 "a b" 3) $ b=$(IFS=, ; echo "${a[*]}") $ echo $b 1,a b,3


liststr="" for item in list do liststr=$item,$liststr done LEN=`expr length $liststr` LEN=`expr $LEN - 1` liststr=${liststr:0:$LEN}

Esto se encarga de la coma extra al final también. No soy un experto de bash. Solo mi 2c, ya que esto es más elemental y comprensible.


s=$(IFS=, eval ''echo "${FOO[*]}"'')