web-dev-qa-db-fra.com

Conversion d'un tableau Bash en une chaîne délimitée

Je voudrais savoir ce qui suit;

  1. Pourquoi l'exemple non fonctionnel donné ne fonctionne pas.
  2. S'il existe d'autres méthodes plus propres que celles données dans l'exemple de travail.

Exemple non fonctionnel

> ids=(1 2 3 4);echo ${ids[*]// /|}
1 2 3 4
> ids=(1 2 3 4);echo ${${ids[*]}// /|}
-bash: ${${ids[*]}// /|}: bad substitution
> ids=(1 2 3 4);echo ${"${ids[*]}"// /|}
-bash: ${"${ids[*]}"// /|}: bad substitution

Exemple de travail

> ids=(1 2 3 4);id="${ids[@]}";echo ${id// /|}
1|2|3|4
> ids=(1 2 3 4); lst=$( IFS='|'; echo "${ids[*]}" ); echo $lst
1|2|3|4

Dans le contexte, la chaîne délimitée à utiliser dans une commande sed pour une analyse ultérieure.

31
koola
# REVISION: 2017-03-14
# Use of read and other bash specific features (bashisms)

Parce que les parenthèses sont utilisées pour délimiter un tablea, pas un string:

ids="1 2 3 4";echo ${ids// /|}
1|2|3|4

Quelques exemples: remplissage de $ids Avec deux chaînes: a b Et c d

ids=("a b" "c d")

echo ${ids[*]// /|}
a|b c|d

IFS='|';echo "${ids[*]}";IFS=$' \t\n'
a b|c d

... et enfin:

IFS='|';echo "${ids[*]// /|}";IFS=$' \t\n'
a|b|c|d

Où le tableau est assemblé, séparé par le premier caractère de $IFS, Mais avec un espace remplacé par | Dans chaque élément du tableau.

Quand vous faites:

id="${ids[@]}"

vous transférez la construction de chaîne à partir de la fusion de arrayids par un espace vers une nouvelle variable de type string.

Remarque: lorsque "${ids[@]}" Donne une chaîne séparée par des espaces , "${ids[*]}" (Avec un star * au lieu du signe at @) affichera une chaîne séparée par le premier caractère de $IFS.

ce que man bash dit:

man -Len -Pcol\ -b bash | sed -ne '/^ *IFS /{N;N;p;q}'
   IFS    The  Internal  Field  Separator  that  is used for Word splitting
          after expansion and to split  lines  into  words  with  the  read
          builtin command.  The default value is ``<space><tab><newline>''.

Jouer avec $IFS:

set | grep ^IFS=
IFS=$' \t\n'
declare -p IFS
declare -- IFS=" 
"
printf "%q\n" "$IFS"
$' \t\n'

Littéralement un space, un tabulation et (ce qui signifie ou) un line-feed. Ainsi, alors que le premier caractère est un espace. l'utilisation de * fera la même chose que @.

Mais:

{
    # OIFS="$IFS"
    # IFS=$': \t\n'
    # unset array 
    # declare -a array=($(echo root:x:0:0:root:/root:/bin/bash))
    IFS=: read -a array < <(echo root:x:0:0:root:/root:/bin/bash)

    echo 1 "${array[@]}"
    echo 2 "${array[*]}"
    OIFS="$IFS" IFS=:
    echo 3 "${array[@]}"
    echo 4 "${array[*]}"
    IFS="$OIFS"
}
1 root x 0 0 root /root /bin/bash
2 root x 0 0 root /root /bin/bash
3 root x 0 0 root /root /bin/bash
4 root:x:0:0:root:/root:/bin/bash

Remarque: La ligne IFS=: read -a array < <(...) utilisera : Comme séparateur, sans définir $IFS De façon permanente. En effet, la ligne de sortie #2 Présente des espaces comme séparateurs.

30
F. Hauri

Votre première question est déjà traitée dans réponse de F. Hauri . Voici une manière canonique de joindre les éléments d'un tableau:

ids=( 1 2 3 4 )
IFS=\| eval 'lst="${ids[*]}"'

Certaines personnes crieront à haute voix que eval est mauvais, mais c'est parfaitement sûr ici, grâce aux guillemets simples. Cela n'a que des avantages: il n'y a pas de sous-shell, IFS n'est pas globalement modifié, il ne coupera pas les sauts de ligne et c'est très simple.

12
gniourf_gniourf

Vous pouvez également utiliser printf, sans aucune commande externe ni besoin de manipuler IFS:

ids=(1 2 3 4)                     # create array
printf -v ids_d '|%s' "${ids[@]}" # yields "|1|2|3|4"
ids_d=${ids_d:1}                  # remove the leading '|'
4
codeforester

Une fonction utilitaire pour diviser le tableau d'arguments avec une chaîne de délimitation:

# Split arguments on delimiter
# @Params
# $1: The delimiter string
# $@: The arguments to delimit
# @Output
# >&1: The arguments separated by the delimiter string
split() {
  (($#<2)) && return 1 # At least 2 arguments required
  local -- delim="$1" str
  shift
  printf -v str "%s$delim" "$@"
  echo "${str:0:-${#delim}}"
}

my_array=( 'Paris' 'Berlin' 'London' 'Brussel' 'Madrid' 'Oslo' )

split ', ' "${my_array[@]}"

Production:

Paris, Berlin, London, Brussel, Madrid, Oslo
0
Léa Gris