¿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.

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

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

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

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"

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.


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}"
}

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.


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.


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í!


$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.


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

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

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

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[@]}"

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

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 )


¿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

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

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 $ 
$

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.


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[@]}"
}

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 .


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}

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[*]}

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

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}"
}

-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.


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


-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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.