Wie konvertiere ich eine Bash-Array-Variable in einen mit Zeilenumbrüchen getrennten String?

50

Ich möchte eine Bash-Array-Variable in eine Datei schreiben, wobei sich jedes Element in einer neuen Zeile befindet. Ich könnte dies mit einer for-Schleife tun, aber gibt es eine andere (sauberere) Möglichkeit, die Elemente mit zu verbinden \n?

ACyclisch
quelle

Antworten:

59

Hier ist ein Weg, der die Bash-Parameter-Erweiterung und ihre IFSspezielle Variable nutzt .

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

Wir verwenden eine Subshell, um zu vermeiden, dass der Wert von IFSin der aktuellen Umgebung überschrieben wird . In dieser Subshell ändern wir dann den Wert von IFSso, dass das erste Zeichen eine neue Zeile ist (unter Verwendung von Anführungszeichen $'...'). Schließlich verwenden wir die Parametererweiterung, um den Inhalt des Arrays als einzelnes Wort auszudrucken. Jedes Element wird durch das erste Zeichen von getrennt IFS.

So erfassen Sie eine Variable:

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

Wenn Ihre Bash neu genug ist (4.2 oder neuer), können (und sollten) Sie immer noch printfmit der -vOption:

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

In beiden Fällen möchten Sie möglicherweise nicht die letzte Zeile in var. Es zu entfernen:

$ var=${var%?}    # Remove the final character of var
chepner
quelle
Danke, gibt es eine Möglichkeit, dies in eine Variable auszugeben?
ACyclic
Aktualisiert, um zu zeigen, wie eine Variable erfasst wird.
Chepner
2
Sollte das letzte Beispiel nicht var=${var%?}stattdessen sein? Dies ist kein regulärer Ausdruck und entspricht daher .nur einem Punkt.
Musiphil
Need zitiert den Namen der Array-Variablen:$ IFS=', ' $ echo ${VAR[*]} first second third $ echo "${VAR[*]}" first,second,third
Seff
28

Sie können printfjedes Array-Element in einer eigenen Zeile drucken:

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

oder

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

oder

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

oder

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

oder

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

oder

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

oder

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

oder

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

Das ist alles, woran ich mich erinnern kann.

Miau
quelle
2

Die obigen Lösungen sind ziemlich genau das Richtige, aber in der ursprünglichen Frage wird nach der Ausgabe in eine Datei gefragt:

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

Anmerkungen: 1) 'echo' liefert den letzten Zeilenumbruch 2) Wenn diese Datei nur noch einmal von der Bash eingelesen wird, kann -p die gewünschte Serialisierung sein.

Brian Chrisman
quelle
2

Verwendung für :

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

Geschichte benutzen ; Beachten Sie, dass dies fehlschlägt, wenn Ihre Werte Folgendes enthalten !:

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

Verwenden des Basisnamens ; Beachten Sie, dass dies fehlschlägt, wenn Ihre Werte Folgendes enthalten /:

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

Mit shuf ; Beachten Sie, dass die Ergebnisse möglicherweise nicht in der richtigen Reihenfolge angezeigt werden:

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

Meine Einstellung, die nur Bash-Builtins verwendet, ohne zu mutieren 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}}"
}

Beispiel

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

Sie können auch ein beliebiges Trennzeichen verwenden:

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

printf Dies scheint der effizienteste Ansatz zu sein, um einen durch Trennzeichen getrennten String aus einem Array zu erstellen:

# 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

Eine noch einfachere Möglichkeit ist:

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

Beispiel

delim=:
arr=(one two three)
printf -v var "%s$delim" "${arr[@]}" # yields one:two:three:
var="${var%$delim}"                  # yields one:two_three
Codeforester
quelle
2
Downvoter, bitte kommentieren Sie, was hier falsch ist.
Codeforester