Ich habe eine Funktion, die ein Array erstellt, und ich möchte das Array an den Aufrufer zurückgeben:
create_array() {
local my_list=("a", "b", "c")
echo "${my_list[@]}"
}
my_algorithm() {
local result=$(create_array)
}
Damit bekomme ich nur einen erweiterten String. Wie kann ich my_list "zurückgeben", ohne etwas Globales zu verwenden?
arrays
bash
global-variables
parameter-passing
Hilfsmethode
quelle
quelle
f () { local a=($(g)); declare -p a; }; g () { local a=(a 'b c' d); echo "${a[@]}"; }; f
Ausgänge "deklarieren -aa = '([0] =" a "[1] =" b "[2] =" c "[3] =" d ")'". Sie werden feststellen, dass Sie anstelle von 3 Elementen jetzt 4 haben."${array[@]}"
Syntax werden Array-Elemente entsprechend inecho
Anführungszeichen gesetzt - es werden jedoch keine Anführungszeichen gedruckt. Daherecho
funktioniert jede verwendete Lösung nur dann ordnungsgemäß, wenn keine Array-Elemente Leerzeichen enthalten. Ich habe Dennis 'Beispiel abstrahiert und es etwas robuster gemacht, um eine praktische, wiederverwendbare Implementierung zu erhalten .f () { local __resultvar=$1; local _local_; _local_=(abc def); declare -p _local_ | sed "s/_local_/$__resultvar/"; }
Mit Bash Version 4.3 und höher können Sie ein nameref verwenden, sodass der Aufrufer den Array-Namen übergeben kann und der Angerufene ein nameref verwenden kann, um das benannte Array indirekt zu füllen .
#!/usr/bin/env bash create_array() { local -n arr=$1 # use nameref for indirection arr=(one "two three" four) } use_array() { local my_array create_array my_array # call function to populate the array echo "inside use_array" declare -p my_array # test the array } use_array # call the main function
Erzeugt die Ausgabe:
inside use_array declare -a my_array=([0]="one" [1]="two three" [2]="four")
Sie können die Funktion auch ein vorhandenes Array aktualisieren lassen:
update_array() { local -n arr=$1 # use nameref for indirection arr+=("two three" four) # update the array } use_array() { local my_array=(one) update_array my_array # call function to update the array }
Dies ist ein eleganterer und effizienterer Ansatz, da keine Befehlsersetzung erforderlich ist
$()
, um die Standardausgabe der aufgerufenen Funktion abzurufen. Es ist auch hilfreich, wenn die Funktion mehr als eine Ausgabe zurückgibt - wir können einfach so viele Namensreferenzen wie die Anzahl der Ausgaben verwenden.Hier ist das Bash-HandbuchFolgendes sagt zu nameref:
quelle
arr
odermy_array
? Beide sind lokal für die jeweiligen Funktionen und daher außerhalb unsichtbar.Bash kann Datenstrukturen nicht als Rückgabewerte weitergeben. Ein Rückgabewert muss ein numerischer Exit-Status zwischen 0 und 255 sein. Sie können jedoch durchaus Befehls- oder Prozessersetzung verwenden, um Befehle an eine eval-Anweisung zu übergeben, wenn Sie dazu neigen.
Das ist meiner Meinung nach selten die Mühe wert. Wenn Sie Datenstrukturen in Bash weitergeben müssen, verwenden Sie eine globale Variable - dafür sind sie da. Wenn Sie dies jedoch aus irgendeinem Grund nicht tun möchten, denken Sie an Positionsparameter.
Ihr Beispiel könnte leicht umgeschrieben werden, um Positionsparameter anstelle globaler Variablen zu verwenden:
use_array () { for idx in "$@"; do echo "$idx" done } create_array () { local array=("a" "b" "c") use_array "${array[@]}" }
Dies alles schafft jedoch eine gewisse unnötige Komplexität. Bash-Funktionen funktionieren im Allgemeinen am besten, wenn Sie sie eher wie Prozeduren mit Nebenwirkungen behandeln und sie nacheinander aufrufen.
# Gather values and store them in FOO. get_values_for_array () { :; } # Do something with the values in FOO. process_global_array_variable () { :; } # Call your functions. get_values_for_array process_global_array_variable
Wenn Sie sich nur Sorgen machen, Ihren globalen Namespace zu verschmutzen, können Sie auch die nicht festgelegte integrierte Funktion verwenden , um eine globale Variable zu entfernen, nachdem Sie damit fertig sind. Lassen Sie my_list anhand Ihres ursprünglichen Beispiels global sein (indem Sie das lokale Schlüsselwort entfernen ) und fügen Sie
unset my_list
es am Ende von my_algorithm hinzu , um nach sich selbst aufzuräumen.quelle
create_array
) den Konsumenten ( ) aufrufen kannuse_array
, nicht umgekehrt.Sie waren mit Ihrer ursprünglichen Lösung nicht so weit weg. Sie hatten einige Probleme, haben ein Komma als Trennzeichen verwendet und die zurückgegebenen Elemente nicht in einer Liste erfasst. Versuchen Sie Folgendes:
my_algorithm() { local result=( $(create_array) ) } create_array() { local my_list=("a" "b" "c") echo "${my_list[@]}" }
In Anbetracht der Kommentare zu eingebetteten Räumen können einige Verbesserungen das Problem
IFS
lösen:my_algorithm() { oldIFS="$IFS" IFS=',' local result=( $(create_array) ) IFS="$oldIFS" echo "Should be 'c d': ${result[1]}" } create_array() { IFS=',' local my_list=("a b" "c d" "e f") echo "${my_list[*]}" }
quelle
create_array
eine Liste wieder, da ich sie verwendet habe[@]
. Wenn ich sie verwendet[*]
hätte, wäre es eine einzelne Zeichenfolge gewesen (sie kann nur eine Zahl zwischen 0 und 255 zurückgeben). Innerhalbmy_algorithm
des Arrays wird der Funktionsaufruf in Klammern gesetzt. Inmy_algorithm
der Variablenresult
befindet sich also ein Array. Ich verstehe den Punkt über eingebettete Räume innerhalb von Werten, die immer Probleme verursachen.arrayIFS=$'\001'
.Verwenden Sie die von Matt McClure entwickelte Technik: http://notes-matthewlmcclure.blogspot.com/2009/12/return-array-from-bash-function-v-2.html
Wenn Sie globale Variablen vermeiden, können Sie die Funktion in einer Pipe verwenden. Hier ist ein Beispiel:
#!/bin/bash makeJunk() { echo 'this is junk' echo '#more junk and "b@d" characters!' echo '!#$^%^&(*)_^&% ^$#@:"<>?/.,\\"'"'" } processJunk() { local -a arr=() # read each input and add it to arr while read -r line do arr+=('"'"$line"'" is junk') done; # output the array as a string in the "declare" representation declare -p arr | sed -e 's/^declare -a [^=]*=//' } # processJunk returns the array in a flattened string ready for "declare" # Note that because of the pipe processJunk cannot return anything using # a global variable returned_string="$(makeJunk | processJunk)" # convert the returned string to an array named returned_array # declare correctly manages spaces and bad characters eval "declare -a returned_array=${returned_string}" for junk in "${returned_array[@]}" do echo "$junk" done
Ausgabe ist:
"this is junk" is junk "#more junk and "b@d" characters!" is junk "!#$^%^&(*)_^&% ^$#@:"<>?/.,\\"'" is junk
quelle
arr+=("value")
anstelle der Indizierung mit${#arr[@]}
. Sehen Sie dies aus dem Grund. Backticks sind veraltet, schwer zu lesen und schwer zu verschachteln. Verwenden Sie$()
stattdessen. Ihre Funktion funktioniert nicht, wenn eine Zeichenfolge inmakeJunk
eine neue Zeile enthält.Eine reine, minimale und robuste Lösung, die auf dem eingebauten 'declare -p' basiert - ohne verrückte globale Variablen
Dieser Ansatz umfasst die folgenden drei Schritte:
myVar="$( declare -p myArray )"
Die Ausgabe der
declare -p
Anweisung kann verwendet werden, um das Array neu zu erstellen. Zum Beispiel könnte die Ausgabe von sodeclare -p myVar
aussehen:declare -a myVar='([0]="1st field" [1]="2nd field" [2]="3rd field")'
${myVar#*=}
Beispiel 1 - Ein Array von einer Funktion zurückgeben
#!/bin/bash # Example 1 - return an array from a function function my-fun () { # set up a new array with 3 fields - note the whitespaces in the # 2nd (2 spaces) and 3rd (2 tabs) field local myFunArray=( "1st field" "2nd field" "3rd field" ) # show its contents on stderr (must not be output to stdout!) echo "now in $FUNCNAME () - showing contents of myFunArray" >&2 echo "by the help of the 'declare -p' builtin:" >&2 declare -p myFunArray >&2 # return the array local myVar="$( declare -p myFunArray )" local IFS=$'\v'; echo "${myVar#*=}" # if the function would continue at this point, then IFS should be # restored to its default value: <space><tab><newline> IFS=' '$'\t'$'\n'; } # main # call the function and recreate the array that was originally # set up in the function eval declare -a myMainArray="$( my-fun )" # show the array contents echo "" echo "now in main part of the script - showing contents of myMainArray" echo "by the help of the 'declare -p' builtin:" declare -p myMainArray # end-of-file
Ausgabe von Beispiel 1:
now in my-fun () - showing contents of myFunArray by the help of the 'declare -p' builtin: declare -a myFunArray='([0]="1st field" [1]="2nd field" [2]="3rd field")' now in main part of the script - showing contents of myMainArray by the help of the 'declare -p' builtin: declare -a myMainArray='([0]="1st field" [1]="2nd field" [2]="3rd field")'
Beispiel 2 - Übergeben Sie ein Array an eine Funktion
#!/bin/bash # Example 2 - pass an array to a function function my-fun () { # recreate the array that was originally set up in the main part of # the script eval declare -a myFunArray="$( echo "$1" )" # note that myFunArray is local - from the bash(1) man page: when used # in a function, declare makes each name local, as with the local # command, unless the ‘-g’ option is used. # IFS has been changed in the main part of this script - now that we # have recreated the array it's better to restore it to the its (local) # default value: <space><tab><newline> local IFS=' '$'\t'$'\n'; # show contents of the array echo "" echo "now in $FUNCNAME () - showing contents of myFunArray" echo "by the help of the 'declare -p' builtin:" declare -p myFunArray } # main # set up a new array with 3 fields - note the whitespaces in the # 2nd (2 spaces) and 3rd (2 tabs) field myMainArray=( "1st field" "2nd field" "3rd field" ) # show the array contents echo "now in the main part of the script - showing contents of myMainArray" echo "by the help of the 'declare -p' builtin:" declare -p myMainArray # call the function and pass the array to it myVar="$( declare -p myMainArray )" IFS=$'\v'; my-fun $( echo "${myVar#*=}" ) # if the script would continue at this point, then IFS should be restored # to its default value: <space><tab><newline> IFS=' '$'\t'$'\n'; # end-of-file
Ausgabe von Beispiel 2:
now in the main part of the script - showing contents of myMainArray by the help of the 'declare -p' builtin: declare -a myMainArray='([0]="1st field" [1]="2nd field" [2]="3rd field")' now in my-fun () - showing contents of myFunArray by the help of the 'declare -p' builtin: declare -a myFunArray='([0]="1st field" [1]="2nd field" [2]="3rd field")'
quelle
Nützliches Beispiel: Geben Sie ein Array von der Funktion zurück
function Query() { local _tmp=`echo -n "$*" | mysql 2>> zz.err`; echo -e "$_tmp"; } function StrToArray() { IFS=$'\t'; set $1; for item; do echo $item; done; IFS=$oIFS; } sql="SELECT codi, bloc, requisit FROM requisits ORDER BY codi"; qry=$(Query $sql0); IFS=$'\n'; for row in $qry; do r=( $(StrToArray $row) ); echo ${r[0]} - ${r[1]} - ${r[2]}; done
quelle
Hier ist eine Lösung ohne externe Array-Referenzen und ohne IFS-Manipulation:
# add one level of single quotes to args, eval to remove squote () { local a=("$@") a=("${a[@]//\'/\'\\\'\'}") # "'" => "'\''" a=("${a[@]/#/\'}") # add "'" prefix to each word a=("${a[@]/%/\'}") # add "'" suffix to each word echo "${a[@]}" } create_array () { local my_list=(a "b 'c'" "\\\"d ") squote "${my_list[@]}" } my_algorithm () { eval "local result=($(create_array))" # result=([0]="a" [1]="b 'c'" [2]=$'\\"d\n') }
quelle
[ Hinweis: Folgendes wurde als Bearbeitung dieser Antwort aus Gründen abgelehnt , die für mich keinen Sinn ergeben (da die Bearbeitung nicht dazu gedacht war, den Autor des Beitrags anzusprechen!), Daher nehme ich den Vorschlag an, sie separat zu machen Antworten.]
Eine einfachere Implementierung von Steve Zobells Anpassung der Technik von Matt McClure verwendet die von BastaMatt vorgeschlagene integrierte Bash (seit Version == 4 ) , um eine Darstellung eines Arrays zu erstellen, das zur Laufzeit in ein Array konvertiert werden kann. (Beachten Sie, dass sowohl und
readarray
readarray
mapfile
derselbe Code denselben Namen haben.) Es werden weiterhin globale Zeichen vermieden (was die Verwendung der Funktion in einer Pipe ermöglicht) und es werden weiterhin böse Zeichen verarbeitet.Einige ausführlichere (z. B. mehr Modularisierung), aber immer noch spielerische Beispiele finden Sie unter bash_pass_arrays_between_functions . Im Folgenden finden Sie einige leicht ausführbare Beispiele, die hier bereitgestellt werden, um zu vermeiden, dass Moderatoren über externe Links nachdenken.
Schneiden Sie den folgenden Block aus und fügen Sie ihn zum Erstellen in ein Bash-Terminal ein
/tmp/source.sh
und/tmp/junk1.sh
:FP='/tmp/source.sh' # path to file to be created for `source`ing cat << 'EOF' > "${FP}" # suppress interpretation of variables in heredoc function make_junk { echo 'this is junk' echo '#more junk and "b@d" characters!' echo '!#$^%^&(*)_^&% ^$#@:"<>?/.,\\"'"'" } ### Use 'readarray' (aka 'mapfile', bash built-in) to read lines into an array. ### Handles blank lines, whitespace and even nastier characters. function lines_to_array_representation { local -a arr=() readarray -t arr # output array as string using 'declare's representation (minus header) declare -p arr | sed -e 's/^declare -a [^=]*=//' } EOF FP1='/tmp/junk1.sh' # path to script to run cat << 'EOF' > "${FP1}" # suppress interpretation of variables in heredoc #!/usr/bin/env bash source '/tmp/source.sh' # to reuse its functions returned_string="$(make_junk | lines_to_array_representation)" eval "declare -a returned_array=${returned_string}" for elem in "${returned_array[@]}" ; do echo "${elem}" done EOF chmod u+x "${FP1}" # newline here ... just hit Enter ...
Lauf
/tmp/junk1.sh
: Ausgabe sollte seinthis is junk #more junk and "b@d" characters! !#$^%^&(*)_^&% ^$#@:"<>?/.,\\"'
Hinweis
lines_to_array_representation
behandelt auch Leerzeilen. Versuchen Sie, den folgenden Block in Ihr Bash-Terminal einzufügen:FP2='/tmp/junk2.sh' # path to script to run cat << 'EOF' > "${FP2}" # suppress interpretation of variables in heredoc #!/usr/bin/env bash source '/tmp/source.sh' # to reuse its functions echo '`bash --version` the normal way:' echo '--------------------------------' bash --version echo # newline echo '`bash --version` via `lines_to_array_representation`:' echo '-----------------------------------------------------' bash_version="$(bash --version | lines_to_array_representation)" eval "declare -a returned_array=${bash_version}" for elem in "${returned_array[@]}" ; do echo "${elem}" done echo # newline echo 'But are they *really* the same? Ask `diff`:' echo '-------------------------------------------' echo 'You already know how to capture normal output (from `bash --version`):' declare -r PATH_TO_NORMAL_OUTPUT="$(mktemp)" bash --version > "${PATH_TO_NORMAL_OUTPUT}" echo "normal output captured to file @ ${PATH_TO_NORMAL_OUTPUT}" ls -al "${PATH_TO_NORMAL_OUTPUT}" echo # newline echo 'Capturing L2AR takes a bit more work, but is not onerous.' echo "Look @ contents of the file you're about to run to see how it's done." declare -r RAW_L2AR_OUTPUT="$(bash --version | lines_to_array_representation)" declare -r PATH_TO_COOKED_L2AR_OUTPUT="$(mktemp)" eval "declare -a returned_array=${RAW_L2AR_OUTPUT}" for elem in "${returned_array[@]}" ; do echo "${elem}" >> "${PATH_TO_COOKED_L2AR_OUTPUT}" done echo "output from lines_to_array_representation captured to file @ ${PATH_TO_COOKED_L2AR_OUTPUT}" ls -al "${PATH_TO_COOKED_L2AR_OUTPUT}" echo # newline echo 'So are they really the same? Per' echo "\`diff -uwB "${PATH_TO_NORMAL_OUTPUT}" "${PATH_TO_COOKED_L2AR_OUTPUT}" | wc -l\`" diff -uwB "${PATH_TO_NORMAL_OUTPUT}" "${PATH_TO_COOKED_L2AR_OUTPUT}" | wc -l echo '... they are the same!' EOF chmod u+x "${FP2}" # newline here ... just hit Enter ...
Führen Sie die
/tmp/junk2.sh
@ -Befehlszeile aus. Ihre Ausgabe sollte meiner ähnlich sein:`bash --version` the normal way: -------------------------------- GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu) Copyright (C) 2013 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. `bash --version` via `lines_to_array_representation`: ----------------------------------------------------- GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu) Copyright (C) 2013 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. But are they *really* the same? Ask `diff`: ------------------------------------------- You already know how to capture normal output (from `bash --version`): normal output captured to file @ /tmp/tmp.Ni1bgyPPEw -rw------- 1 me me 308 Jun 18 16:27 /tmp/tmp.Ni1bgyPPEw Capturing L2AR takes a bit more work, but is not onerous. Look @ contents of the file you're about to run to see how it's done. output from lines_to_array_representation captured to file @ /tmp/tmp.1D6O2vckGz -rw------- 1 me me 308 Jun 18 16:27 /tmp/tmp.1D6O2vckGz So are they really the same? Per `diff -uwB /tmp/tmp.Ni1bgyPPEw /tmp/tmp.1D6O2vckGz | wc -l` 0 ... they are the same!
quelle
Ich habe verschiedene Implementierungen ausprobiert und keine Arrays mit Elementen mit Leerzeichen erhalten ... weil sie alle verwendet werden mussten
echo
.# These implementations only work if no array items contain spaces. use_array() { eval echo '(' \"\${${1}\[\@\]}\" ')'; } use_array() { local _array="${1}[@]"; echo '(' "${!_array}" ')'; }
Lösung
Dann stieß ich auf Dennis Williamsons Antwort . Ich habe seine Methode in die folgenden Funktionen integriert, damit sie a) ein beliebiges Array akzeptieren und b) zum Übergeben, Duplizieren und Anhängen von Arrays verwendet werden können.
# Print array definition to use with assignments, for loops, etc. # varname: the name of an array variable. use_array() { local r=$( declare -p $1 ) r=${r#declare\ -a\ *=} # Strip keys so printed definition will be a simple list (like when using # "${array[@]}"). One side effect of having keys in the definition is # that when appending arrays (i.e. `a1+=$( use_array a2 )`), values at # matching indices merge instead of pushing all items onto array. echo ${r//\[[0-9]\]=} } # Same as use_array() but preserves keys. use_array_assoc() { local r=$( declare -p $1 ) echo ${r#declare\ -a\ *=} }
Dann können andere Funktionen ein Array mit abfangbaren Ausgaben oder indirekten Argumenten zurückgeben.
# catchable output return_array_by_printing() { local returnme=( "one" "two" "two and a half" ) use_array returnme } eval test1=$( return_array_by_printing ) # indirect argument return_array_to_referenced_variable() { local returnme=( "one" "two" "two and a half" ) eval $1=$( use_array returnme ) } return_array_to_referenced_variable test2 # Now both test1 and test2 are arrays with three elements
quelle
sed
, können Sie wahrscheinlich Bash Regex - Operator verwenden=~
und${BASH_REMATCH}
an seinem Platz.=~
und durchzuführen${BASH_REMATCH}
. Das Übereinstimmungsmuster ist jedoch so einfach, dass Regex nicht einmal benötigt wird. Ich habe die Funktion aktualisiert, um stattdessen die Variablensubstitution zu verwendensed
.${r//\[[0-9]\]=}
funktioniert nicht mit Arrays mit mehr als 9 Elementen (sie ersetzt nicht [10] =). Sie können extglob aktivieren und${r//\[+([0-9])\]=}
stattdessen verwenden.B=(" a " "" " " "b" " c " " d ") eval A=$(use_array B)
Ich brauchte kürzlich eine ähnliche Funktionalität, daher ist das Folgende eine Mischung aus den Vorschlägen von RashaMatt und Steve Zobell .
Soweit ich sehen kann, bleiben die Zeichenfolgen intakt und die Leerzeichen bleiben erhalten.
#!bin/bash function create-array() { local somearray=("aaa" "bbb ccc" "d" "e f g h") for elem in "${somearray[@]}" do echo "${elem}" done } mapfile -t resa <<< "$(create-array)" # quick output check declare -p resa
Noch ein paar Variationen…
#!/bin/bash function create-array-from-ls() { local somearray=("$(ls -1)") for elem in "${somearray[@]}" do echo "${elem}" done } function create-array-from-args() { local somearray=("$@") for elem in "${somearray[@]}" do echo "${elem}" done } mapfile -t resb <<< "$(create-array-from-ls)" mapfile -t resc <<< "$(create-array-from-args 'xxx' 'yy zz' 't s u' )" sentenceA="create array from this sentence" sentenceB="keep this sentence" mapfile -t resd <<< "$(create-array-from-args ${sentenceA} )" mapfile -t rese <<< "$(create-array-from-args "$sentenceB" )" mapfile -t resf <<< "$(create-array-from-args "$sentenceB" "and" "this words" )" # quick output check declare -p resb declare -p resc declare -p resd declare -p rese declare -p resf
quelle
Ich habe kürzlich eine Besonderheit in BASH entdeckt, dass eine Funktion direkten Zugriff auf die Variablen hat, die in den Funktionen deklariert sind, die höher im Aufrufstapel liegen. Ich habe gerade erst darüber nachgedacht, wie diese Funktion genutzt werden kann (sie verspricht sowohl Vorteile als auch Gefahren), aber eine offensichtliche Anwendung ist eine Lösung für den Geist dieses Problems.
Ich würde auch lieber einen Rückgabewert erhalten, als eine globale Variable zu verwenden, wenn ich die Erstellung eines Arrays delegiere. Es gibt mehrere Gründe für meine Präferenz, unter anderem, um zu vermeiden, dass ein bereits vorhandener Wert möglicherweise gestört wird, und um zu vermeiden, dass ein Wert zurückbleibt, der beim späteren Zugriff möglicherweise ungültig ist. Es gibt zwar Problemumgehungen für diese Probleme, aber am einfachsten ist es, wenn die Variable den Gültigkeitsbereich verlässt, wenn der Code damit fertig ist.
Meine Lösung stellt sicher, dass das Array bei Bedarf verfügbar ist und bei der Rückkehr der Funktion verworfen wird, und lässt eine globale Variable mit demselben Namen ungestört.
#!/bin/bash myarr=(global array elements) get_an_array() { myarr=( $( date +"%Y %m %d" ) ) } request_array() { declare -a myarr get_an_array "myarr" echo "New contents of local variable myarr:" printf "%s\n" "${myarr[@]}" } echo "Original contents of global variable myarr:" printf "%s\n" "${myarr[@]}" echo request_array echo echo "Confirm the global myarr was not touched:" printf "%s\n" "${myarr[@]}"
Hier ist die Ausgabe dieses Codes:
Wenn die Funktion request_array Anrufe get_an_array , get_an_array kann direkt die eingestellte myArr Variable, die lokal ist request_array . Da myarr mit erstellt wird
declare
, ist es lokal für request_array und verlässt daher den Bereich, wenn request_array zurückkehrt.Obwohl diese Lösung einen Wert nicht buchstäblich zurückgibt, schlage ich vor, dass sie insgesamt die Versprechen eines echten Funktionsrückgabewerts erfüllt.
quelle
Dies kann auch durch einfaches Übergeben einer Array-Variablen an die Funktion und Zuweisen von Array-Werten zu dieser Variable erfolgen. Verwenden Sie dann diese Variable außerhalb der Funktion. Zum Beispiel.
create_array() { local __resultArgArray=$1 local my_list=("a" "b" "c") eval $__resultArgArray="("${my_list[@]}")" } my_algorithm() { create_array result echo "Total elements in the array: ${#result[@]}" for i in "${result[@]}" do echo $i done } my_algorithm
quelle
Der einfachste Weg, den ich gefunden habe
my_function() { array=(one two three) echo ${array[@]} } result=($(my_function)) echo ${result[0]} echo ${result[1]} echo ${result[2]}
quelle
/bin/bash
nicht/bin/sh
Wenn Ihre Quelldaten mit jedem Listenelement in einer separaten Zeile formatiert sind, ist die integrierte
mapfile
Methode eine einfache und elegante Möglichkeit, eine Liste in ein Array einzulesen:$ list=$(ls -1 /usr/local) # one item per line $ mapfile -t arrayVar <<<"$list" # -t trims trailing newlines $ declare -p arrayVar | sed 's#\[#\n[#g' declare -a arrayVar='( [0]="bin" [1]="etc" [2]="games" [3]="include" [4]="lib" [5]="man" [6]="sbin" [7]="share" [8]="src")'
Beachten Sie, dass Sie wie beim
read
eingebauten System normalerweise nicht *mapfile
in einer Pipeline (oder Subshell) verwenden würden, da die zugewiesene Array-Variable für nachfolgende Anweisungen nicht verfügbar wäre (* es sei denn, die Bash-Jobsteuerung ist deaktiviert undshopt -s lastpipe
festgelegt).$ help mapfile mapfile: mapfile [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array] Read lines from the standard input into an indexed array variable. Read lines from the standard input into the indexed array variable ARRAY, or from file descriptor FD if the -u option is supplied. The variable MAPFILE is the default ARRAY. Options: -n count Copy at most COUNT lines. If COUNT is 0, all lines are copied. -O origin Begin assigning to ARRAY at index ORIGIN. The default index is 0. -s count Discard the first COUNT lines read. -t Remove a trailing newline from each line read. -u fd Read lines from file descriptor FD instead of the standard input. -C callback Evaluate CALLBACK each time QUANTUM lines are read. -c quantum Specify the number of lines read between each call to CALLBACK. Arguments: ARRAY Array variable name to use for file data. If -C is supplied without -c, the default quantum is 5000. When CALLBACK is evaluated, it is supplied the index of the next array element to be assigned and the line to be assigned to that element as additional arguments. If not supplied with an explicit origin, mapfile will clear ARRAY before assigning to it. Exit Status: Returns success unless an invalid option is given or ARRAY is readonly or not an indexed array.
quelle
Ich würde vorschlagen, an einen Codeblock weiterzuleiten, um die Werte eines Arrays festzulegen. Die Strategie ist POSIX-kompatibel, sodass Sie sowohl Bash als auch Zsh erhalten und nicht das Risiko von Nebenwirkungen wie bei den veröffentlichten Lösungen eingehen.
i=0 # index for our new array declare -a arr # our new array # pipe from a function that produces output by line ls -l | { while read data; do i=$i+1; arr[$i]="$data"; done } # example of reading that new array for row in "${arr[@]}"; do echo "$row"; done
Dies funktioniert für
zsh
undbash
und wird nicht durch Leerzeichen oder Sonderzeichen beeinflusst. Im Fall des OP wird die Ausgabe durch Echo transformiert, sodass ein Array nicht ausgegeben, sondern gedruckt wird (wie in anderen Shell-Funktionen erwähnt, werden Status und keine Werte zurückgegeben). Wir können es in einen Pipeline-fähigen Mechanismus ändern:create_array() { local my_list=("a", "b", "c") for row in "${my_list[@]}"; do echo "$row" done } my_algorithm() { i=0 declare -a result create_array | { while read data; do i=$i+1; result[$i]="$data"; done } }
Wenn dies der Fall ist, könnte man den
create_array
Pipeline-Prozess entfernenmy_algorithm
und die beiden Funktionen miteinander verkettenquelle
Sie können die
declare -p
Methode auch einfacher verwenden, indem Sie diedeclare -a
doppelte Auswertung nutzen, wenn der Wert eine Zeichenfolge ist (keine echten Parens außerhalb der Zeichenfolge):# return_array_value returns the value of array whose name is passed in. # It turns the array into a declaration statement, then echos the value # part of that statement with parentheses intact. You can use that # result in a "declare -a" statement to create your own array with the # same value. Also works for associative arrays with "declare -A". return_array_value () { declare Array_name=$1 # namespace locals with caps to prevent name collision declare Result Result=$(declare -p $Array_name) # dehydrate the array into a declaration echo "${Result#*=}" # trim "declare -a ...=" from the front } # now use it. test for robustness by skipping an index and putting a # space in an entry. declare -a src=([0]=one [2]="two three") declare -a dst="$(return_array_value src)" # rehydrate with double-eval declare -p dst > declare -a dst=([0]="one" [2]="two three") # result matches original
Überprüfung des Ergebnisses
declare -p dst
ergibtdeclare -a dst=([0]="one" [2]="two three")"
, was zeigt, dass diese Methode sowohl spärliche Arrays als auch Einträge mit einem IFS-Zeichen (Leerzeichen) korrekt behandelt.Das erste ist, das Quell-Array zu dehydrieren, indem
declare -p
eine gültige Bash-Deklaration generiert wird. Da es sich bei der Deklaration um eine vollständige Anweisung handelt, einschließlich "deklarieren" und des Variablennamens, entfernen wir diesen Teil von vorne mit${Result#*=}
und lassen die Klammern mit den Indizes und Werten darin :([0]="one" [2]="two three")
.Anschließend wird das Array rehydriert, indem dieser Wert Ihrer eigenen Deklarationsanweisung zugeführt wird, in der Sie den Namen des Arrays auswählen. Es beruht auf der Tatsache, dass die rechte Seite der
dst
Array-Deklaration eine Zeichenfolge mit Klammern innerhalb der Zeichenfolge ist und keine echten Klammern in der Deklaration selbst, zdeclare -a dst=( "true parens outside string" )
. B. nicht . Dies löst ausdeclare
, dass die Zeichenfolge zweimal ausgewertet wird, einmal in eine gültige Anweisung mit Klammern (und Anführungszeichen im beibehaltenen Wert) und eine weitere für die tatsächliche Zuweisung. Dh es wertet zuerstdeclare -a dst=([0]="one" [2]="two three")
, dann auswertet , dass als Aussage.Beachten Sie, dass dieses doppelte Bewertungsverhalten spezifisch für das
-a
und ist-A
Optionen von declare.Oh, und diese Methode funktioniert auch mit assoziativen Arrays. Wechseln Sie einfach
-a
zu-A
.Da diese Methode auf stdout basiert, funktioniert sie wie Pipelines über Subshell-Grenzen hinweg wie Pipelines.
Ich diskutiere diese Methode ausführlicher in meinem Blog-Beitrag
quelle
Sie können dies versuchen
my_algorithm() { create_array list for element in "${list[@]}" do echo "${element}" done } create_array() { local my_list=("1st one" "2nd two" "3rd three") eval "${1}=()" for element in "${my_list[@]}" do eval "${1}+=(\"${element}\")" done } my_algorithm
Die Ausgabe ist
quelle