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
.
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
konsolebox
estilo :)function join { local IFS=$1; __="${*:2}"; }
ofunction 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 :)$d
en el especificador de formato deprintf
. 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 úselasprintf -- "$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.Otra solución más:
Editar: igual pero para el separador de longitud variable de varios caracteres:
fuente
printf -v bar ",%s" "${foo[@]}"
. Es unofork
menos (en realidadclone
). Incluso se bifurcan lectura de un archivo:printf -v bar ",%s" $(<infile)
.$separator
no contiene%s
o por ejemplo, puede hacer que suprintf
robusta:printf "%s%s" "$separator" "${foo[@]}"
.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.printf "%s" "${foo[@]/#/$separator}"
.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.fuente
bar=$( IFS=, ; echo "${foo[*]}" )
@
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.*
. En la página de manual de bash, busque "Parámetros especiales" y busque la explicación junto a*
:Tal vez, por ejemplo,
fuente
echo "-${IFS}-"
(las llaves separan los guiones del nombre de la variable).echo $IFS
hace lo mismo.)Sorprendentemente, mi solución aún no se ha dado :) Esta es la forma más simple para mí. No necesita una función:
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.fuente
Aquí hay una función Bash 100% pura que hace el trabajo:
Mira:
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:fuente
join_ret
una 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.$(...)
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).Esto no es muy diferente de las soluciones existentes, pero evita usar una función separada, no se modifica
IFS
en el shell principal y está todo en una sola línea:Resultando en
Limitación: el separador no puede tener más de un carácter.
fuente
Sin usar comandos externos:
Advertencia, supone que los elementos no tienen espacios en blanco.
fuente
echo ${FOO[@]} | tr ' ' ','
Yo haría eco de la matriz como una cadena, luego transformaría los espacios en saltos de línea, y luego usaría
paste
para 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í!
fuente
$FOO
Sin embargo, es solo el primer elemento de la matriz. Además, esto se rompe para los elementos de la matriz que contienen espacios.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.
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.
fuente
fuente
@Q
podría escapar de los valores unidos de una mala interpretación cuando tienen unafoo=("a ," "b ' ' c" "' 'd e" "f " ";" "ls -latr"); s=$(IFS=, eval 'echo "${foo[*]@Q}"'); echo "${s}"
'a ,','b '\'' '\'' c',''\'' '\''d e','f ',';','ls -latr '
Solución printf que acepta separadores de cualquier longitud (basado en @ no importa la respuesta)
fuente
printf
especificador de formato . (Por ejemplo,%s
sin querer en$sep
causará problemas.sep
se 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 enecho
ella.function join_by { printf -v __ "${1//\%/%%}%s" "${@:2}"; __=${__:${#1}}; }
fuente
HISTSIZE=0
?paste -sd,
no se trata del uso de la historia.HISTSIZE=0
, pruébalo.Versión más corta de la respuesta superior:
Uso:
fuente
join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '%s' "${@/#/$d}"; }
join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '$d%s' "${@}"; }
esto funciona con el uso:join_strings 'delim' "${array[@]}"
o sinjoin_strings 'delim' ${array[@]}
Combina lo mejor de todos los mundos hasta ahora con la siguiente idea.
Esta pequeña obra maestra es
Ejemplos:
fuente
join_ws ,
(sin argumentos) resultados incorrectos,,
. 2.join_ws , -e
no genera nada incorrectamente (eso se debe a que está utilizando incorrectamente enecho
lugar deprintf
). En realidad, no sé por qué anunciaste el uso de enecho
lugar deprintf
:echo
está notoriamente roto yprintf
es una construcción robusta.En este momento estoy usando:
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 )
fuente
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.Mi intento.
fuente
Use perl para separadores de caracteres múltiples:
O en una línea:
fuente
join
nombre está en conflicto con algo de basuraOS X
... lo llamaríaconjoined
, ¿o tal vezjackie_joyner_kersee
?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.
Esta belleza por concepción es
Ejemplos adicionales:
fuente
En caso de que los elementos que desee unir no sean una matriz, solo una cadena separada por espacios, puede hacer algo como esto:
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:
En my_script, necesito hacer "SELECT * FROM table WHERE name IN ('aa', 'bb', 'cc', 'dd'). Entonces el comando anterior será útil.
fuente
printf -v bar ...
lugar de tener que ejecutar el ciclo printf en una subshell y capturar la salida.Aquí hay uno que admite la mayoría de los shells compatibles con POSIX:
fuente
local
) en absoluto.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.
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.
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:
Tomando nota de que en la documentación de
${parameter}
,parameter
se 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 .
fuente
__
como nombre de variable? Hace que el código sea realmente ilegible.Este enfoque se ocupa de los espacios dentro de los valores, pero requiere un bucle:
fuente
Si construye la matriz en un bucle, aquí hay una manera simple:
fuente
x=${"${arr[*]}"// /,}
Esta es la forma más corta de hacerlo.
Ejemplo,
fuente
bash: ${"${arr[*]}"// /,}: bad substitution
Quizás tarde para la fiesta, pero esto funciona para mí:
fuente
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
printf
nada. Tampoco se vuelve realmente feo prescindir de él.Al menos, me ha funcionado hasta ahora sin problemas.
Por ejemplo,
join \| *.sh
que, digamos que estoy en mi~
directorio, salidasutilities.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.
fuente
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
fuente
o
fuente