Come faccio a convertire una variabile di array bash in una stringa delimitata da newline?


50

Voglio scrivere una variabile di matrice bash in un file, con ogni elemento su una nuova riga. Potrei farlo con un ciclo for, ma esiste un altro modo (più pulito) per unire gli elementi \n?

Risposte:


59

Ecco un modo che utilizza l'espansione dei parametri bash e la sua IFSvariabile speciale.

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

Usiamo una subshell per evitare di sovrascrivere il valore IFSnell'ambiente attuale. In quella subshell, modifichiamo quindi il valore in IFSmodo che il primo carattere sia una nuova riga (usando le $'...'virgolette). Infine, utilizziamo l'espansione dei parametri per stampare il contenuto dell'array come una singola parola; ogni elemento è separato dal primo carattere di IFS.

Per acquisire in una variabile:

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

Se il tuo bash è abbastanza nuovo (4.2 o successivo), puoi (e dovresti) usare ancora printfcon l' -vopzione:

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

In entrambi i casi, potresti non voler inserire la nuova riga finale var. Per rimuoverlo:

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

Grazie, c'è un modo per generare questo in una variabile?
ACyclic

Aggiornato per mostrare come acquisire in una variabile.
Chepner,

2
L'ultimo esempio non dovrebbe essere var=${var%?}invece? Questa non è un'espressione regolare, quindi .corrisponderà solo a un carattere punto.
musiphil,

Bisogno di citare il nome della variabile array:$ IFS=', ' $ echo ${VAR[*]} first second third $ echo "${VAR[*]}" first,second,third
Seff

28

È possibile utilizzare printfper stampare ciascun elemento dell'array sulla propria riga:

 $ 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[@]}"

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[@]}"

questo è tutto ciò che posso ricordare finora.


2

Le soluzioni sopra sono praticamente tutto, ma la domanda originale richiede l'output su file:

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

Note: 1) 'echo' fornisce la nuova riga finale 2) Se questo file verrà nuovamente letto da bash, allora dichiarare -p potrebbe essere la serializzazione desiderata.


2

Utilizzando per :

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

Utilizzando la cronologia ; nota che questo fallirà se i tuoi valori contengono !:

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

Utilizzando basename ; nota che questo fallirà se i tuoi valori contengono /:

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

Usando shuf ; si noti che i risultati potrebbero non essere pubblicati in ordine:

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

0

La mia opinione, usando solo i builtin di Bash, senza mutare 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}}"
}

Esempio

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

Puoi anche usare qualsiasi separatore:

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

-1

printf sembra essere l'approccio più efficiente per la creazione di una stringa delimitata da un array:

# 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

Un modo ancora più semplice per farlo è:

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

Esempio

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

2
Downvoter, per favore commenta cosa non va qui.
codeforester,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.