Si j'ai un tableau comme celui-ci dans Bash:
FOO=( a b c )
Comment joindre les éléments avec des virgules? Par exemple, produire a,b,c
.
Solution de réécriture de Pascal Pilz en fonction en 100% pur Bash (pas de commandes externes):
function join_by { local IFS="$1"; shift; echo "$*"; }
Par exemple,
join_by , a "b c" d #a,b c,d
join_by / var local tmp #var/local/tmp
join_by , "${FOO[@]}" #a,b,c
Alternativement, nous pouvons utiliser printf pour prendre en charge les délimiteurs à plusieurs caractères, en utilisant l'idée de @gniourf_gniourf
function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; }
Par exemple,
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
style :)function join { local IFS=$1; __="${*:2}"; }
oufunction join { IFS=$1 eval '__="${*:2}"'; }
. Ensuite, utilisez__
après. Oui, je suis celui qui promeut l'utilisation de__
comme variable de résultat;) (et une variable d'itération commune ou variable temporaire). Si le concept arrive sur un site wiki Bash populaire, ils m'ont copié :)$d
dans le spécificateur de format deprintf
. Vous pensez que vous êtes en sécurité depuis que vous vous êtes «échappé»,%
mais il y a d'autres mises en garde: lorsque le délimiteur contient une barre oblique inverse (par exemple,\n
) ou lorsque le délimiteur commence par un trait d'union (et peut-être d'autres auxquels je ne peux pas penser maintenant). Vous pouvez bien sûr les corriger (remplacer les contre-obliques par des contre-obliques doubles et les utiliserprintf -- "$d%s"
), mais à un moment donné, vous sentirez que vous vous battez contre le shell au lieu de travailler avec. C'est pourquoi, dans ma réponse ci-dessous, j'ai ajouté le délimiteur aux conditions à joindre.Encore une autre solution:
Edit: idem mais pour séparateur à longueur variable multi-caractères:
la source
printf -v bar ",%s" "${foo[@]}"
. C'est un defork
moins (en faitclone
). Il est même bifurquer la lecture d' un fichier:printf -v bar ",%s" $(<infile)
.$separator
ne contient pas%s
ou tel, vous pouvez rendre votreprintf
robuste:printf "%s%s" "$separator" "${foo[@]}"
.printf "%s%s"
utiliserait le séparateur dans la première instance UNIQUEMENT ensemble de sortie, puis concaténerait simplement le reste des arguments.printf "%s" "${foo[@]/#/$separator}"
.IFS=; regex="${foo[*]/#/$separator}"
. À ce stade, cela devient essentiellement la réponse de gniourf_gniourf qui IMO est plus propre depuis le début, c'est-à-dire, en utilisant la fonction pour limiter la portée des changements IFS et des variables temporaires.la source
bar=$( IFS=, ; echo "${foo[*]}" )
@
au lieu de*
, comme dans$(IFS=, ; echo "${foo[@]}")
? Je peux voir que le*
conserve déjà l'espace blanc dans les éléments, encore une fois je ne sais pas comment, car il@
est généralement nécessaire à cet effet.*
. Dans la page de manuel de bash, recherchez «Paramètres spéciaux» et recherchez l'explication à côté de*
:Peut-être, par exemple,
la source
echo "-${IFS}-"
(les accolades séparent les tirets du nom de la variable).echo $IFS
fait la même chose.Étonnamment, ma solution n'est pas encore donnée :) C'est le moyen le plus simple pour moi. Il n'a pas besoin d'une fonction:
Remarque: Cette solution a été observée pour bien fonctionner en mode non-POSIX. En mode POSIX , les éléments sont toujours joints correctement, mais
IFS=,
deviennent permanents.la source
Voici une fonction Bash 100% pure qui fait le travail:
Regardez:
Cela préserve même les nouvelles lignes de fin et n'a pas besoin d'un sous-shell pour obtenir le résultat de la fonction. Si vous n'aimez pas le
printf -v
(pourquoi ne l'aimeriez-vous pas?) Et en passant un nom de variable, vous pouvez bien sûr utiliser une variable globale pour la chaîne retournée:la source
join_ret
une variable locale, puis en l'écho à la fin. Cela permet à join () d'être utilisé de la manière habituelle de script shell, par exemple$(join ":" one two three)
, et ne nécessite pas de variable globale.$(...)
trims suivent les nouvelles lignes; donc si le dernier champ du tableau contient des sauts de ligne de fin, ceux-ci seraient coupés (voir la démo où ils ne sont pas coupés avec ma conception).Ce n'est pas trop différent des solutions existantes, mais cela évite d'utiliser une fonction distincte, ne modifie pas
IFS
dans le shell parent et est tout sur une seule ligne:résultant en
Limitation: le séparateur ne peut pas dépasser un caractère.
la source
Sans aucune commande externe:
Attention, cela suppose que les éléments n'ont pas d'espaces.
la source
echo ${FOO[@]} | tr ' ' ','
Je voudrais faire écho au tableau sous forme de chaîne, puis transformer les espaces en sauts de ligne, puis utiliser
paste
pour joindre tout en une seule ligne comme suit:tr " " "\n" <<< "$FOO" | paste -sd , -
Résultats:
a,b,c
Cela me semble être le plus rapide et le plus propre!
la source
$FOO
n'est que le premier élément du tableau. En outre, cela se rompt pour les éléments de tableau contenant des espaces.Avec la réutilisation de @, la solution n'a pas d'importance, mais avec une seule instruction en évitant la sous-station $ {: 1} et le besoin d'une variable intermédiaire.
printf a 'La chaîne de format est réutilisée aussi souvent que nécessaire pour satisfaire les arguments.' dans ses pages de manuel, afin que les concaténations des chaînes soient documentées. Ensuite, l'astuce consiste à utiliser la longueur de la LISTE pour hacher le dernier spérateur, car la coupe ne conservera que la longueur de la LISTE lorsque les champs compteront.
la source
la source
@Q
pourrait échapper aux valeurs jointes d'une interprétation erronée quand ils ont un menuisier en eux:foo=("a ," "b ' ' c" "' 'd e" "f " ";" "ls -latr"); s=$(IFS=, eval 'echo "${foo[*]@Q}"'); echo "${s}"
sorties'a ,','b '\'' '\'' c',''\'' '\''d e','f ',';','ls -latr '
solution printf qui accepte des séparateurs de n'importe quelle longueur (basé sur @ n'a pas d'importance réponse)
la source
printf
spécificateur de format (par exemple,%s
involontairement dans$sep
entraînera des problèmes.sep
peut être désinfecté avec${sep//\%/%%}
. J'aime mieux votre solution que${bar#${sep}}
ou${bar%${sep}}
(alternative). C'est bien s'il est converti en une fonction qui stocke le résultat dans une variable générique comme__
, et nonecho
elle.function join_by { printf -v __ "${1//\%/%%}%s" "${@:2}"; __=${__:${#1}}; }
la source
HISTSIZE=0
?paste -sd,
ne concerne pas l'utilisation de l'histoire.HISTSIZE=0
- essayez-le.Version plus courte de la première réponse:
Usage:
la source
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' "${@}"; }
Cela fonctionne avec l'utilisation:join_strings 'delim' "${array[@]}"
ou sans guillemets:join_strings 'delim' ${array[@]}
Combinez le meilleur de tous les mondes jusqu'à présent avec l'idée suivante.
Ce petit chef-d'œuvre est
Exemples:
la source
join_ws ,
(sans argument) sort à tort,,
. 2.join_ws , -e
ne produit rien à tort (c'est parce que vous utilisez à tortecho
au lieu deprintf
) En fait, je ne sais pas pourquoi vous avez annoncé l'utilisation deecho
au lieu deprintf
:echo
est notoirement cassé etprintf
est un robuste intégré.En ce moment j'utilise:
Ce qui fonctionne, mais (dans le cas général) se cassera horriblement si les éléments du tableau ont un espace en eux.
(Pour ceux qui sont intéressés, il s'agit d'un script wrapper autour de pep8.py )
la source
ARGS="--ignore $(echo "${TO_IGNORE[@]}" | tr ' ' ',')"
. L'opérateur$()
est plus puissant que les backtics (permet l'imbrication de$()
et""
). L'encapsulation${TO_IGNORE[@]}
avec des guillemets doubles devrait également aider.Ma tentative.
la source
Utilisez perl pour les séparateurs multicaractères:
Ou en une seule ligne:
la source
join
nom entre en conflit avec des conneriesOS X
.. je l'appelleraisconjoined
, ou peutjackie_joyner_kersee
- être ?Merci @gniourf_gniourf pour les commentaires détaillés sur ma combinaison des meilleurs mondes jusqu'à présent. Désolé pour le code de publication non conçu et testé à fond. Voici un meilleur essai.
Cette beauté par conception est
Exemples supplémentaires:
la source
Dans le cas où les éléments que vous souhaitez joindre ne sont pas un tableau juste une chaîne séparée par des espaces, vous pouvez faire quelque chose comme ceci:
par exemple, mon cas d'utilisation est que certaines chaînes sont passées dans mon script shell et je dois l'utiliser pour exécuter une requête SQL:
Dans mon_script, je dois faire "SELECT * FROM table WHERE name IN ('aa', 'bb', 'cc', 'dd'). La commande ci-dessus sera alors utile.
la source
printf -v bar ...
au lieu d'avoir à exécuter la boucle printf dans un sous-shell et capturer la sortie.En voici un que la plupart des shells compatibles POSIX prennent en charge:
la source
local
) du tout.L'utilisation d'indirection variable pour se référer directement à un tableau fonctionne également. Les références nommées peuvent également être utilisées, mais elles ne sont devenues disponibles qu'en 4.3.
L'avantage de l'utilisation de cette forme de fonction est que le séparateur peut être facultatif (par défaut, le premier caractère par défaut
IFS
, qui est un espace; peut-être en faire une chaîne vide si vous le souhaitez), et cela évite d'étendre les valeurs deux fois (d'abord lorsqu'il est passé en tant que paramètres, et en second lieu"$@"
à l'intérieur de la fonction).Cette solution n'exige pas non plus que l'utilisateur appelle la fonction à l'intérieur d'une substitution de commande - qui invoque un sous-shell, pour obtenir une version jointe d'une chaîne affectée à une autre variable.
N'hésitez pas à utiliser un nom plus confortable pour la fonction.
Cela fonctionne de 3.1 à 5.0-alpha. Comme observé, l'indirection variable ne fonctionne pas seulement avec des variables mais aussi avec d'autres paramètres.
Les tableaux et les éléments de tableau sont également des paramètres (entités qui stockent de la valeur), et les références aux tableaux sont également des références techniques aux paramètres. Et tout comme le paramètre spécial
@
,array[@]
fait également une référence valide.Les formes d'expansion modifiées ou sélectives (comme l'expansion de la sous-chaîne) qui dévient la référence du paramètre lui-même ne fonctionnent plus.
Mise à jour
Dans la version finale de Bash 5.0, l' indirection variable est déjà appelée expansion indirecte et son comportement est déjà explicitement documenté dans le manuel:
Prenant note que dans la documentation de
${parameter}
,parameter
est appelé "un paramètre shell comme décrit (dans) PARAMETRES ou une référence de tableau ". Et dans la documentation des tableaux, il est mentionné que "Tout élément d'un tableau peut être référencé en utilisant${name[subscript]}
". Cela fait__r[@]
une référence de tableau.Join by arguments version
Voir mon commentaire dans la réponse de Riccardo Galli .
la source
__
comme nom de variable? Rend le code vraiment illisible.Cette approche prend soin des espaces dans les valeurs, mais nécessite une boucle:
la source
Si vous construisez le tableau en boucle, voici un moyen simple:
la source
x=${"${arr[*]}"// /,}
C'est le moyen le plus court de le faire.
Exemple,
la source
bash: ${"${arr[*]}"// /,}: bad substitution
Peut-être en retard pour la fête, mais cela fonctionne pour moi:
la source
Peut-être que je manque quelque chose d'évident, car je suis un nouveau dans le bash / zsh, mais il me semble que vous n'avez pas besoin du tout d'utiliser
printf
. Il ne devient pas vraiment moche de s'en passer.Au moins, cela a fonctionné pour moi jusqu'à présent sans problème.
Par exemple,
join \| *.sh
qui, disons que je suis dans mon~
répertoire, sortutilities.sh|play.sh|foobar.sh
. Suffisant pour moi.EDIT: Ceci est essentiellement la réponse de Nil Geisweiller , mais généralisé en fonction.
la source
Cela prend également en charge la virgule supplémentaire à la fin. Je ne suis pas un expert bash. Juste mon 2c, car c'est plus élémentaire et plus compréhensible
la source
ou
la source