Como faço para converter uma variável do array bash em uma string delimitada por novas linhas?


50

Eu quero escrever uma variável de matriz bash em um arquivo, com cada elemento em uma nova linha. Eu poderia fazer isso com um loop for, mas existe outra maneira (mais limpa) de juntar os elementos \n?

Respostas:


59

Aqui está uma maneira que utiliza a expansão de parâmetros do bash e sua IFSvariável especial.

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

Usamos um subshell para evitar a substituição do valor IFSno ambiente atual. Nesse subshell, modificamos o valor de IFSpara que o primeiro caractere seja uma nova linha (usando $'...'aspas). Finalmente, usamos a expansão de parâmetros para imprimir o conteúdo da matriz como uma única palavra; cada elemento é separado pelo primeiro caractere de IFS.

Para capturar em uma variável:

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

Se o seu bash for novo o suficiente (4.2 ou posterior), você pode (e deve) ainda usar printfcom a -vopção:

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

Em ambos os casos, talvez você não queira a nova linha final var. Para removê-lo:

$ var=${var%?}    # Remove the final character of var

Obrigado, existe uma maneira de produzir isso para uma variável?
ACyclic 16/08/12

Atualizado para mostrar como capturar em uma variável.
Chepner 16/08/12

2
O último exemplo não deveria ser var=${var%?}? Esta não é uma expressão regular, portanto .corresponderá apenas a um caractere de ponto.
Musiphil 13/09/2013

A necessidade cita o nome da variável da matriz:$ IFS=', ' $ echo ${VAR[*]} first second third $ echo "${VAR[*]}" first,second,third
Seff 16/06

28

Você pode usar printfpara imprimir cada item da matriz em sua própria linha:

 $ System=('s1' 's2' 's3' 's4 4 4')
 $ printf "%s\n"  "${System[@]}"
s1
s2
s3
s4 4 4

7
awk -v sep='\n' 'BEGIN{ORS=OFS="";for(i=1;i<ARGC;i++){print ARGV[i],ARGC-i-1?sep:""}}' "${arr[@]}"

ou

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

ou

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

ou

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

ou

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

ou

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

ou

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

ou

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

é tudo o que posso lembrar até agora.


2

As soluções acima são praticamente isso, mas a pergunta original pede a saída para o arquivo:

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

Notas: 1) 'echo' fornece a nova linha final 2) Se este arquivo for lido novamente pelo bash novamente, então declare -p pode ser a serialização desejada.


2

Usando para :

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

Usando a história ; observe que isso falhará se seus valores contiverem !:

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

Usando o basename ; observe que isso falhará se seus valores contiverem /:

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

Usando shuf ; observe que os resultados podem não sair em ordem:

shuf -e "${alpha[@]}"

0

Minha opinião , usando apenas Bash builtins, sem alterar 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}}"
}

Exemplo

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

Você também pode usar qualquer separador:

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

-1

printf parece ser a abordagem mais eficiente para criar uma string delimitada a partir de uma 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

Uma maneira ainda mais simples de fazer isso é:

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

Exemplo

delim=:
arr=(one two three)
printf -v var "%s$delim" "${arr[@]}" # yields one:two:three:
var="${var%$delim}"                  # yields one:two_three

2
Downvoter, por favor comente o que está errado aqui.
precisa saber é o seguinte
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.