¿Cómo convierto una variable de matriz bash en una cadena delimitada con líneas nuevas?

50

Quiero escribir una variable de matriz bash en un archivo, con cada elemento en una nueva línea. Podría hacer esto con un bucle for, pero ¿hay otra forma (más limpia) de unir los elementos \n?

Acíclico
fuente

Respuestas:

59

Aquí hay una manera que utiliza la expansión de parámetros bash y su IFSvariable especial.

$ System=('s1' 's2' 's3' 's4 4 4')
$ ( IFS=$'\n'; echo "${System[*]}" )

Usamos un subshell para evitar sobrescribir el valor de IFSen el entorno actual. En ese subshell, modificamos el valor de IFSmodo que el primer carácter sea una nueva línea (usando $'...'comillas). Finalmente, usamos la expansión de parámetros para imprimir el contenido de la matriz como una sola palabra; cada elemento está separado por el primer personaje de IFS.

Para capturar a una variable:

$ var=$( IFS=$'\n'; echo "${System[*]}" )

Si su bash es lo suficientemente nuevo (4.2 o posterior), aún puede (y debería) usarlo printfcon la -vopción:

$ printf -v var "%s\n" "${System[@]}"

En cualquier caso, es posible que no desee la nueva línea final var. Para eliminarlo:

$ var=${var%?}    # Remove the final character of var
chepner
fuente
Gracias, ¿hay alguna manera de enviar esto a una variable?
Acíclico
Actualizado para mostrar cómo capturar a una variable.
chepner
2
¿No debería ser el último ejemplo var=${var%?}? Esta no es una expresión regular, por .lo que solo coincidirá con un carácter de punto.
musiphil
La necesidad cita el nombre de la variable de matriz:$ IFS=', ' $ echo ${VAR[*]} first second third $ echo "${VAR[*]}" first,second,third
Seff
28

Puede usar printfpara imprimir cada elemento de la matriz en su propia línea:

 $ System=('s1' 's2' 's3' 's4 4 4')
 $ printf "%s\n"  "${System[@]}"
s1
s2
s3
s4 4 4
prisa
fuente
7
awk -v sep='\n' 'BEGIN{ORS=OFS="";for(i=1;i<ARGC;i++){print ARGV[i],ARGC-i-1?sep:""}}' "${arr[@]}"

o

perl -le 'print join "\n",@ARGV' "${arr[@]}"

o

python -c 'import sys;print "\n".join(sys.argv[1:])' "${arr[@]}"

o

sh -c 'IFS=$'\''\n'\'';echo "$*"' '' "${arr[@]}"

o

lua <(echo 'print(table.concat(arg,"\n"))') "${arr[@]}"

o

tclsh <(echo 'puts [join $argv "\n"]') "${arr[@]}"

o

php -r 'echo implode("\n",array_slice($argv,1));' -- "${arr[@]}"

o

ruby -e 'puts ARGV.join("\n")' "${arr[@]}"

eso es todo lo que puedo recordar hasta ahora.

maullar
fuente
2

Las soluciones anteriores son más o menos eso, pero la pregunta original pide resultados para archivar:

$ a=(a b c d e)
$ ( IFS=$'\n'; echo "${a[*]}" ) > /tmp/file
$ cat /tmp/file
a
b
c
d
e
$

Notas: 1) 'echo' proporciona la nueva línea final 2) Si bash vuelve a leer este archivo nuevamente, entonces declare -p puede ser la serialización deseada.

Brian Chrisman
fuente
2

Utilizando para :

for each in "${alpha[@]}"
do
  echo "$each"
done

Usando la historia ; tenga en cuenta que esto fallará si sus valores contienen !:

history -p "${alpha[@]}"

Usando basename ; tenga en cuenta que esto fallará si sus valores contienen /:

basename -a "${alpha[@]}"

Usando shuf ; tenga en cuenta que los resultados pueden no aparecer en orden:

shuf -e "${alpha[@]}"
Steven Penny
fuente
0

Mi opinión , usando solo Bash Builtins, sin mutar IFS:

# $1  separator
# $2… strings
join_strings () {
    declare separator="$1";
    declare -a args=("${@:2}");
    declare result;
    printf -v result '%s' "${args[@]/#/$separator}";
    printf '%s' "${result:${#separator}}"
}

Ejemplo

$ join_strings $'\n' "a b c" "d e f" "g h i"
a b c
d e f
g h i

También puedes usar cualquier separador:

$ join_strings '===' "a b c" "d e f" "g h i"
a b c===d e f===g h i
jcayzac
fuente
-1

printf parece ser el enfoque más eficiente para crear una cadena delimitada a partir de una matriz:

# create a delimited string; note that printf doesn't put the trailing delimiter
# need to save and restore IFS
# it is prudent to put the whole logic on a single line so as to minimize the risk of future code changes breaking the sequence of saving/restoring of IFS
oldIFS=$IFS; IFS=$'\n'; printf -v var "${arr[*]}"; IFS=$oldIFS

# print string to file; note that the newline is required in the format string because printf wouldn't put a trailing delimiter (which is a good thing)
printf '%s\n' "$var" > file

Una forma aún más simple de hacer esto es:

delim=$'\n'
printf -v var "%s$delim" "${arr[@]}" # create a delimited string
var="${var%$delim}"                  # remove the trailing delimiter

Ejemplo

delim=:
arr=(one two three)
printf -v var "%s$delim" "${arr[@]}" # yields one:two:three:
var="${var%$delim}"                  # yields one:two_three
codeforester
fuente
2
Downvoter, por favor comente lo que está mal aquí.
codeforester