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

417

Si tengo una matriz como esta en Bash:

FOO=( a b c )

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

David Wolever
fuente

Respuestas:

571

Solución de reescritura de Pascal Pilz como 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 caracteres múltiples, utilizando 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
Nicholas Sushkin
fuente
99
Use esto para separadores de caracteres múltiples: function join {perl -e '$ s = shift @ARGV; print join ($ s, @ARGV); ' PS } unirse ',' abc # a, b, c
Daniel Patru
44
@dpatru de todos modos para hacer esa fiesta pura?
CMCDragonkai
44
@puchu Lo que no funciona son los separadores de caracteres múltiples. Decir "el espacio no funciona" hace que parezca que unirse a un espacio no funciona. Lo hace.
Eric
66
Esto promueve subcapas de desove si se almacena la salida en variable. Use konsoleboxestilo :) function join { local IFS=$1; __="${*:2}"; }o function join { IFS=$1 eval '__="${*:2}"'; }. Luego use __después. Sí, soy el que promueve el uso de __como variable de resultado;) (y una variable de iteración común o variable temporal). Si el concepto llega a un sitio wiki popular de Bash, me copiaron :)
konsolebox
66
No ponga la expansión $den el especificador de formato de printf. Cree que está a salvo desde que "escapó", %pero hay otras advertencias: cuando el delimitador contiene una barra invertida (por ejemplo, \n) o cuando el delimitador comienza con un guión (y tal vez otros que no se me ocurren ahora). Por supuesto, puede solucionarlos (reemplace las barras diagonales inversas por barras diagonales dobles y úselas printf -- "$d%s"), pero en algún momento sentirá que está luchando contra el caparazón en lugar de trabajar con él. Es por eso que, en mi respuesta a continuación, antepuse el delimitador a los términos a unir.
gniourf_gniourf
206

Otra solución más:

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

echo $bar

Editar: igual pero para el separador de longitud variable de varios 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
no importa
fuente
77
+1. ¿Qué pasa con printf -v bar ",%s" "${foo[@]}". Es uno forkmenos (en realidad clone). Incluso se bifurcan lectura de un archivo: printf -v bar ",%s" $(<infile).
TrueY
14
En lugar de orar $separatorno contiene %so por ejemplo, puede hacer que su printfrobusta: printf "%s%s" "$separator" "${foo[@]}".
musiphil
55
@musiphil Incorrecto. De bash man: "El formato se reutiliza según sea necesario para consumir todos los argumentos. El uso de dos marcadores de posición de formato como en printf "%s%s"usaría el separador en el conjunto de salida SOLO en primera instancia, y luego simplemente concatenaría el resto de argumentos.
AnyDev
3
@AndrDevEK: Gracias por descubrir el error. En cambio, sugeriría algo así printf "%s" "${foo[@]/#/$separator}".
musiphil
2
@musiphil, gracias. ¡Si! Entonces printf se vuelve redundante y esa línea se puede reducir a IFS=; regex="${foo[*]/#/$separator}". En este punto, esto se convierte esencialmente en la respuesta de gniourf_gniourf, que IMO es más limpia desde el principio, es decir, usar la función para limitar el alcance de los cambios IFS y las variables temporales.
AnyDev
145
$ foo=(a "b c" d)
$ bar=$(IFS=, ; echo "${foo[*]}")
$ echo "$bar"
a,b c,d
Pascal Pilz
fuente
3
Las comillas dobles externas y las comillas dobles alrededor del colon no son necesarias. Solo son necesarias las comillas dobles internas:bar=$( IFS=, ; echo "${foo[*]}" )
ceving
8
+1 para la solución más compacta que no necesita bucles, que no necesita comandos externos y que no impone restricciones adicionales sobre el conjunto de caracteres de los argumentos.
ceving
22
Me gusta la solución, pero solo funciona si IFS es un personaje
Jayen
8
¿Alguna idea de por qué esto no funciona si se usa en @lugar de *, como en $(IFS=, ; echo "${foo[@]}")? Puedo ver que *ya conserva el espacio en blanco en los elementos, nuevamente no estoy seguro de cómo, ya @que generalmente se requiere para este motivo.
haridsv
10
Encontré la respuesta a mi propia pregunta arriba. La respuesta es que solo se reconoce IFS *. En la página de manual de bash, busque "Parámetros especiales" y busque la explicación junto a *:
haridsv
66

Tal vez, por ejemplo,

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

echo "$FOOJOIN"
Martin Clayton
fuente
3
Si haces eso, piensa que IFS- es la variable. Tienes que hacer echo "-${IFS}-"(las llaves separan los guiones del nombre de la variable).
Pausado hasta nuevo aviso.
1
Todavía obtuve el mismo resultado (acabo de poner los guiones para ilustrar el punto ... echo $IFShace lo mismo.)
David Wolever
41
Dicho esto, esto todavía parece funcionar ... Entonces, como la mayoría de las cosas con Bash, fingiré que lo entiendo y seguiré con mi vida.
David Wolever
2
Un "-" no es un carácter válido para un nombre de variable, por lo que el shell hace lo correcto cuando usa $ IFS-, no necesita $ {IFS} - (bash, ksh, sh y zsh en linux y solaris también de acuerdo).
Idelic
2
@David, la diferencia entre tu eco y el de Dennis es que ha usado citas dobles. El contenido de IFS se usa 'en la entrada' como una declaración de caracteres separadores de palabras, por lo que siempre obtendrá una línea vacía sin comillas.
Martin
30

Sorprendentemente, mi solución aún no se ha dado :) 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 se IFS=,vuelven permanentes.

konsolebox
fuente
desafortunadamente solo funciona para delimitadores de un solo carácter
maoizm
24

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 le gusta printf -v(¿por qué no le gustaría?) Y pasa un nombre de variable, por supuesto, puede 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}"
}
gniourf_gniourf
fuente
1
Su última solución es muy buena, pero podría hacerse más limpia haciendo join_retuna variable local y luego haciendo eco al final. Esto permite que join () se use de la forma habitual de scripting de shell, por ejemplo $(join ":" one two three), y no requiere una variable global.
James Sneeringer
1
@JamesSneeringer Usé este diseño a propósito para evitar subcapas. En las secuencias de comandos de shell, a diferencia de muchos otros lenguajes, las variables globales utilizadas de esa manera no son necesariamente algo malo; especialmente si están aquí para ayudar a evitar subcapas. Por otra parte, $(...)recorta las nuevas líneas; así que si el último campo de la matriz contiene nuevas líneas finales, estas se recortarán (vea la demostración donde no se recortan con mi diseño).
gniourf_gniourf
Esto funciona con separadores de caracteres múltiples, lo que me hace feliz ^ _ ^
spiffytech
Para abordar el "¿por qué no le gustaría printf -v?": En Bash, las variables locales no son realmente locales de función, por lo que puede hacer cosas como esta. (Llame a la función f1 con la variable local x, que a su vez llama a la función f2 que modifica x, que se declara local dentro del alcance de f1) Pero no es realmente cómo deberían funcionar las variables locales. Si las variables locales realmente son locales (o se supone que lo son, por ejemplo, en un script que debe funcionar tanto en bash como en ksh), entonces causa problemas con todo este esquema de "devolver un valor almacenándolo en la variable con este nombre".
tetsujin
15

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

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

Resultando en

a,b,c

Limitación: el separador no puede tener más de un carácter.

Benjamin W.
fuente
13

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, supone que los elementos no tienen espacios en blanco.

Nil Geisweiller
fuente
44
Si no desea utilizar una variable intermedia, puede hacerlo aún más corto:echo ${FOO[@]} | tr ' ' ','
jesjimher
2
No entiendo los votos negativos. Es una solución mucho más compacta y fácil de leer que otras publicadas aquí, y está claramente advertido de que no funciona cuando hay espacios.
jesjimher
12

Yo haría eco de la matriz como una cadena, luego transformaría los espacios en saltos de línea, y luego usaría pastepara unir todo en una línea de esta manera:

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

Resultados:

a,b,c

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

Yanick Girouard
fuente
$FOOSin embargo, es solo el primer elemento de la matriz. Además, esto se rompe para los elementos de la matriz que contienen espacios.
Benjamin W.
9

Con la reutilización de @ no importa la solución, sino con una declaración evitando la sustitución $ {: 1} y la necesidad de una variable intermediaria.

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 man, de modo que se documentan las concatenaciones de las cadenas. Luego, el truco es usar la longitud de la LISTA para cortar el último sperator, ya que el corte retendrá solo la longitud de la LISTA a medida que los campos cuentan.

Valija
fuente
7
s=$(IFS=, eval 'echo "${FOO[*]}"')
anguila ghEEz
fuente
8
Deberías dar cuerpo a tu respuesta.
Joce
El mejor de todos. ¡¡Gracias!!
peter pan gz
44
Desearía poder rechazar esta respuesta porque abre un agujero de seguridad y porque destruirá espacios en los elementos.
anguila ghEEz
1
@bxm de hecho, parece preservar espacios y no permite escapar del contexto de los argumentos de eco. Pensé que sumar @Qpodría escapar de los valores unidos de una mala interpretación cuando tienen una foo=("a ," "b ' ' c" "' 'd e" "f " ";" "ls -latr"); s=$(IFS=, eval 'echo "${foo[*]@Q}"'); echo "${s}"'a ,','b '\'' '\'' c',''\'' '\''d e','f ',';','ls -latr '
combinación
1
Evite las soluciones que utilizan subscapas a menos que sea necesario.
konsolebox
5

Solución 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
Riccardo Galli
fuente
Esto produce una salida con una coma inicial.
Mark Renouf
La última barra = $ {bar: $ {# sep}} elimina el separador. Acabo de copiar y pegar en un shell bash y funciona. ¿Qué caparazón estás usando?
Riccardo Galli
2
Cualquier printf especificador de formato . (Por ejemplo, %ssin querer en $sepcausará problemas.
Peter.O
sepse puede desinfectar con ${sep//\%/%%}. Me gusta su solución mejor que ${bar#${sep}}o ${bar%${sep}}(alternativa). Esto es bueno si se convierte en una función que almacena el resultado en una variable genérica como __, y no en echoella.
konsolebox
function join_by { printf -v __ "${1//\%/%%}%s" "${@:2}"; __=${__:${#1}}; }
konsolebox
4
$ set a 'b c' d

$ history -p "$@" | paste -sd,
a,b c,d
Steven Penny
fuente
Esto debería estar en la parte superior.
Eric Walker
66
Esto no debería estar en la parte superior: ¿y si HISTSIZE=0?
har-wradim
@ har-wradim, el truco paste -sd,no se trata del uso de la historia.
Veda
@Veda No, se trata del uso de la combinación, y no funcionaría si HISTSIZE=0, pruébalo.
har-wradim
4

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

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

Uso:

joinStrings "$myDelimiter" "${myArray[@]}"
Camilo Martin
fuente
1
Una versión más larga, pero no es necesario hacer una copia de una porción de los argumentos en una variable de matriz:join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '%s' "${@/#/$d}"; }
Rockallite
Otra versión más: join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '$d%s' "${@}"; } esto funciona con el uso:join_strings 'delim' "${array[@]}" o sin join_strings 'delim' ${array[@]}
citar
4

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 shell, 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
invitado
fuente
1
No es tan agradable: al menos 2 problemas: 1. join_ws ,(sin argumentos) resultados incorrectos ,,. 2. join_ws , -eno genera nada incorrectamente (eso se debe a que está utilizando incorrectamente en echolugar de printf). En realidad, no sé por qué anunciaste el uso de en echolugar de printf: echoestá notoriamente roto y printfes una construcción robusta.
gniourf_gniourf
1

En este momento 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 de envoltura alrededor de pep8.py )

David Wolever
fuente
¿de dónde obtienes esos valores de matriz? si lo está codificando así, ¿por qué no simplemente foo = "a, b, c"?
ghostdog74
En este caso, en realidad estoy codificando los valores, pero quiero ponerlos en una matriz para poder comentar cada uno individualmente. He actualizado la respuesta para mostrarte lo que quiero decir.
David Wolever, el
Suponiendo que en realidad se está usando bash, esto podría funcionar mejor: ARGS="--ignore $(echo "${TO_IGNORE[@]}" | tr ' ' ',')". El operador $()es más poderoso que el backtics (permite anidar $()y ""). Envolver ${TO_IGNORE[@]}con comillas dobles también debería ayudar.
kevinarpe
1

Mi intento.

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

Use 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
Daniel Patru
fuente
funciona para mí, aunque el joinnombre está en conflicto con algo de basura OS X... lo llamaría conjoined, ¿o tal vez jackie_joyner_kersee?
Alex Gray
1

Gracias @gniourf_gniourf por los comentarios detallados sobre mi combinación de los mejores mundos hasta ahora. Perdón por publicar código que no está 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

  • (todavía) 100% puro bash (gracias por señalar explícitamente que printf también está integrado. No estaba al tanto de esto antes ...)
  • trabaja con delimitadores de caracteres múltiples
  • más compacto y más completo y esta vez cuidadosamente pensado y probado a largo plazo 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 en separador y / o parámetros, y casos extremos , y casos de esquina y otras objeciones 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 sufren 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 $ 
$
invitado
fuente
1

En caso de que los elementos que desee unir 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 table WHERE name IN ('aa', 'bb', 'cc', 'dd'). Entonces el comando anterior será útil.

Dexin Wang
fuente
Puede usar en printf -v bar ...lugar de tener que ejecutar el ciclo printf en una subshell y capturar la salida.
codeforester
todas las elegantes soluciones votadas arriba no funcionaron, pero la cruda sí lo hizo por mí (Y)
ishandutta2007
1

Aquí hay uno que admite la mayoría de los shells compatibles con POSIX:

join_by() {
    # Usage:  join_by "||" a b c d
    local arg arr=() sep="$1"
    shift
    for arg in "$@"; do
        if [ 0 -lt "${#arr[@]}" ]; then
            arr+=("${sep}")
        fi
        arr+=("${arg}") || break
    done
    printf "%s" "${arr[@]}"
}
usuario541686
fuente
Está bien el código Bash, pero POSIX no tiene matrices (o local) en absoluto.
Anders Kaseorg
@Anders: Sí, lo aprendido de la manera difícil hace muy poco :( lo dejaré para ahora, sin embargo ya que la mayoría conchas compatibles POSIX parecen apoyar matrices.
user541686
1

Usar indirección variable para referirse directamente a una matriz también funciona. También se pueden usar referencias con nombre, pero solo estuvieron disponibles en 4.3.

La ventaja de usar esta forma de función es que puede tener el separador opcional (por defecto es el primer carácter predeterminado IFS, que es un espacio; quizás lo convierta en 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 invoca una subshell, para obtener una versión unida de una cadena asignada a otra variable.

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.

Siéntase libre de usar un nombre más cómodo para la función.

Esto funciona desde 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 enumerados 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 hace 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.

Actualizar

En la versión de lanzamiento de Bash 5.0, la indirección variable ya se denomina expansión indirecta y su comportamiento ya está explícitamente documentado en el manual:

Si el primer carácter del parámetro es un signo de exclamación (!), Y el parámetro no es una referencia de nombre, introduce un nivel de indirección. Bash usa el valor formado al expandir el resto del parámetro como el nuevo parámetro; esto se expande y ese valor se usa en el resto de la expansión, en lugar de la expansión del parámetro original. Esto se conoce como expansión indirecta.

Tomando nota de que en la documentación de ${parameter}, parameterse conoce como "un parámetro de shell como se describe (en) PARÁMETROS o una referencia de matriz ". Y en la documentación de las matrices, se menciona que "Cualquier elemento de una matriz puede ser referenciado usando ${name[subscript]}". Esto hace __r[@]una referencia de matriz.

Únete por versión de argumentos

Vea mi comentario en la respuesta de Riccardo Galli .

konsolebox
fuente
2
¿Hay alguna razón específica para usar __como nombre de variable? Hace que el código sea realmente ilegible.
PesaEl
@PesaThe Es solo una preferencia. Prefiero usar nombres genéricos para una variable de retorno. Otros nombres no genéricos se atribuyen a funciones específicas y requiere memorización. Llamar a múltiples funciones que devuelven valores en diferentes variables puede hacer que el código sea menos fácil de seguir. El uso de un nombre genérico obligaría al scripter a transferir el valor de la variable de retorno a la variable adecuada para evitar conflictos, y hace que el código termine siendo más legible ya que se vuelve explícito dónde van los valores devueltos. Sin embargo, hago algunas excepciones a esa regla.
konsolebox
0

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}
dengel
fuente
0

Si construye 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[*]}
Ian Kelling
fuente
0

x=${"${arr[*]}"// /,}

Esta es la forma más corta de hacerlo.

Ejemplo,

arr=(1 2 3 4 5)
x=${"${arr[*]}"// /,}
echo $x  # output: 1,2,3,4,5
usuario31986
fuente
1
Esto no funciona correctamente para cadenas con espacios: `t = (a" b c "d); echo $ {t [2]} (imprime "b c"); echo $ {"$ {t [*]}" // /,} (imprime a, b, c, d)
kounoupis
77
bash: ${"${arr[*]}"// /,}: bad substitution
Cameron Hudson
0

Quizás tarde para la fiesta, pero esto funciona para mí:

function joinArray() {
  local delimiter="${1}"
  local output="${2}"
  for param in ${@:3}; do
    output="${output}${delimiter}${param}"
  done

  echo "${output}"
}
TacB0sS
fuente
-1

Quizás me estoy perdiendo algo obvio, ya que soy un novato en todo lo relacionado con bash / zsh, pero me parece que no necesitas usar printfnada. Tampoco se vuelve realmente feo prescindir de él.

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

Al menos, me ha funcionado hasta ahora sin problemas.

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

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

Jordán
fuente
1
No soy el votante, pero manipular un global en una función parece bastante raro.
tripleee
-2
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 en bash. Solo mi 2c, ya que esto es más elemental y comprensible

byte_array
fuente
-2
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
maullar
fuente