Jeśli mam taką tablicę w Bash:
FOO=( a b c )
Jak połączyć elementy przecinkami? Na przykład produkcja a,b,c
.
Przepisywanie rozwiązania przez Pascala Pilza jako funkcja w 100% czystej Bash (bez zewnętrznych poleceń):
function join_by { local IFS="$1"; shift; echo "$*"; }
Na przykład,
join_by , a "b c" d #a,b c,d
join_by / var local tmp #var/local/tmp
join_by , "${FOO[@]}" #a,b,c
Alternatywnie możemy użyć printf do obsługi ograniczników wieloznakowych, korzystając z pomysłu @gniourf_gniourf
function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; }
Na przykład,
join_by , a b c #a,b,c
join_by ' , ' a b c #a , b , c
join_by ')|(' a b c #a)|(b)|(c
join_by ' %s ' a b c #a %s b %s c
join_by $'\n' a b c #a<newline>b<newline>c
join_by - a b c #a-b-c
join_by '\' a b c #a\b\c
konsolebox
stylu :)function join { local IFS=$1; __="${*:2}"; }
lubfunction join { IFS=$1 eval '__="${*:2}"'; }
. Następnie użyj__
po. Tak, promuję użycie__
jako zmiennej wynikowej;) (i wspólnej zmiennej iteracyjnej lub zmiennej tymczasowej). Jeśli pomysł$d
w specyfikatorze formatuprintf
. Myślisz, że jesteś bezpieczny, ponieważ „uciekłeś”,%
ale istnieją inne zastrzeżenia: kiedy ogranicznik zawiera odwrotny ukośnik (np.\n
) Lub kiedy ogranicznik zaczyna się od myślnika (i może innych, o których nie mogę teraz myśleć). Możesz je oczywiście naprawić (zastąpić odwrotnymi ukośnikami podwójnymi odwrotnymi ukośnikami i użyćprintf -- "$d%s"
), ale w pewnym momencie poczujesz, że walczysz z powłoką zamiast z nią pracować. Dlatego w poniższej odpowiedzi wstawiłem separator do warunków, które należy dołączyć.Jeszcze inne rozwiązanie:
Edycja: tak samo, ale dla wieloznakowego separatora o zmiennej długości:
źródło
printf -v bar ",%s" "${foo[@]}"
. To o jedenfork
mniej (właściwieclone
). Jest nawet rozwidlone odczytu pliku:printf -v bar ",%s" $(<infile)
.$separator
nie zawiera%s
lub takich, można zrobić swójprintf
wytrzymałość:printf "%s%s" "$separator" "${foo[@]}"
.printf "%s%s"
spowoduje użycie separatora TYLKO pierwszego zestawu danych wyjściowych, a następnie po prostu połączy pozostałe argumenty.printf "%s" "${foo[@]/#/$separator}"
.IFS=; regex="${foo[*]/#/$separator}"
. W tym momencie zasadniczo staje się to odpowiedzią gniourf_gniourf, która IMO jest czystsza od samego początku, to znaczy za pomocą funkcji ograniczającej zakres zmian IFS i zmienne temp.źródło
bar=$( IFS=, ; echo "${foo[*]}" )
@
zamiast*
, jak w$(IFS=, ; echo "${foo[@]}")
? Widzę, że*
już zachowuje białe znaki w elementach, znowu nie jestem pewien, jak to zrobić , ponieważ@
zwykle jest to wymagane ze względu na to.*
. Na stronie podręcznika użytkownika bash wyszukaj „Parametry specjalne” i poszukaj wyjaśnienia obok*
:Może np.
źródło
echo "-${IFS}-"
(nawiasy klamrowe oddzielają myślniki od nazwy zmiennej).echo $IFS
robi to samo.Co zaskakujące, moje rozwiązanie nie zostało jeszcze podane :) To dla mnie najprostszy sposób. Nie potrzebuje funkcji:
Uwaga: Zaobserwowano, że to rozwiązanie działa dobrze w trybie innym niż POSIX. W trybie POSIX elementy są nadal prawidłowo łączone, ale
IFS=,
stają się trwałe.źródło
Oto w 100% czysta funkcja Bash, która wykonuje zadanie:
Popatrz:
Zachowuje to nawet końcowe znaki nowego wiersza i nie wymaga podpowłoki, aby uzyskać wynik funkcji. Jeśli nie podoba ci się
printf -v
(dlaczego by ci się nie podobało?) I przekazujesz nazwę zmiennej, możesz oczywiście użyć zmiennej globalnej dla zwracanego ciągu:źródło
join_ret
zmienną lokalną, a następnie odbijając ją na końcu. Pozwala to na użycie join () w zwykły sposób skryptowania powłoki, np.$(join ":" one two three)
I nie wymaga zmiennej globalnej.$(...)
przycina końcowe znaki nowej linii; więc jeśli ostatnie pole tablicy zawiera końcowe znaki nowej linii, zostaną one przycięte (zobacz wersję demonstracyjną, w której nie zostały one przycięte przez mój projekt).Nie różni się to zbytnio od istniejących rozwiązań, ale unika używania osobnej funkcji, nie modyfikuje się
IFS
w powłoce nadrzędnej i znajduje się w jednym wierszu:powodując
Ograniczenie: separator nie może być dłuższy niż jedna postać.
źródło
Bez użycia zewnętrznych poleceń:
Ostrzeżenie, zakłada, że elementy nie mają białych znaków.
źródło
echo ${FOO[@]} | tr ' ' ','
Chciałbym powtórzyć tablicę jako ciąg znaków, a następnie przekształcić spacje w kanały liniowe, a następnie użyć
paste
do połączenia wszystkiego w jednym wierszu w taki sposób:tr " " "\n" <<< "$FOO" | paste -sd , -
Wyniki:
a,b,c
To wydaje mi się najszybsze i najczystsze!
źródło
$FOO
jest tylko pierwszym elementem tablicy. Ponadto powoduje to uszkodzenie elementów tablicy zawierających spacje.Ponowne użycie @ nie ma znaczenia rozwiązanie, ale z jedną instrukcją, unikając podstacji $ {: 1} i potrzeby zmiennej pośredniej.
printf ma „Ciąg formatu jest ponownie wykorzystywany tak często, jak to konieczne, aby spełnić argumenty”. na stronach podręcznika, aby udokumentować konkatenacje łańcuchów. Zatem sztuczką jest użycie długości LISTY do posiekania ostatniego speratora, ponieważ cięcie zachowa tylko długość LISTY w miarę liczenia pól.
źródło
źródło
@Q
może uchronić połączone wartości przed błędną interpretacją, gdy mają w sobiefoo=("a ," "b ' ' c" "' 'd e" "f " ";" "ls -latr"); s=$(IFS=, eval 'echo "${foo[*]@Q}"'); echo "${s}"
'a ,','b '\'' '\'' c',''\'' '\''d e','f ',';','ls -latr '
printf rozwiązanie, które akceptuje separatory dowolnej długości (na podstawie @ nie ma znaczenia odpowiedź)
źródło
printf
specyfikator formatu (np.%s
Niezamierzenie w$sep
spowoduje problemy).sep
można się zdezynfekować${sep//\%/%%}
. I jak rozwiązania lepsze niż${bar#${sep}}
lub${bar%${sep}}
(alternatywnie). Jest to przydatne, jeśli zostanie przekonwertowane na funkcję przechowującą wynik w zmiennej ogólnej, takiej jak__
, a nie wecho
nim.function join_by { printf -v __ "${1//\%/%%}%s" "${@:2}"; __=${__:${#1}}; }
źródło
HISTSIZE=0
?paste -sd,
na wykorzystaniu historii.HISTSIZE=0
- wypróbuj.Krótsza wersja najwyższej odpowiedzi:
Stosowanie:
źródło
join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '%s' "${@/#/$d}"; }
join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '$d%s' "${@}"; }
Działa z użyciem:join_strings 'delim' "${array[@]}"
lub bez cudzysłowu:join_strings 'delim' ${array[@]}
Połącz najlepsze ze wszystkich dotychczasowych światów z następującym pomysłem.
To małe arcydzieło
Przykłady:
źródło
join_ws ,
(bez argumentów) niepoprawnie wyprowadza,,
. 2.join_ws , -e
błędnie wyprowadza nic (to dlatego, że niewłaściwie używaszecho
zamiastprintf
). W rzeczywistości nie wiem, dlaczego reklamowałeś użycieecho
zamiastprintf
:echo
jest notorycznie zepsuty iprintf
jest solidnym wbudowanym programem.Obecnie używam:
Które działa, ale (w ogólnym przypadku) przerwie się okropnie, jeśli elementy tablicy będą miały spację.
(Dla zainteresowanych jest to skrypt otoki wokół pep8.py )
źródło
ARGS="--ignore $(echo "${TO_IGNORE[@]}" | tr ' ' ',')"
. Operator$()
ma większą moc niż backtics (umożliwia zagnieżdżanie$()
i""
).${TO_IGNORE[@]}
Pomóc powinno także zawijanie podwójnych cudzysłowów.Moja próba
źródło
Użyj perla dla separatorów wieloznakowych:
Lub w jednym wierszu:
źródło
join
nazwa koliduje z jakimś gównem naOS X
... zadzwoniłbymconjoined
, a możejackie_joyner_kersee
?Dziękuję @gniourf_gniourf za szczegółowe komentarze na temat mojej dotychczasowej kombinacji najlepszych światów. Przepraszamy za kod pocztowy, który nie został dokładnie zaprojektowany i przetestowany. Oto lepsza próba.
To piękno z założenia jest
Dodatkowe przykłady:
źródło
Jeśli elementy, które chcesz połączyć, nie są tablicą, ale ciągiem znaków oddzielonych spacją, możesz zrobić coś takiego:
na przykład, moim przypadkiem użycia jest to, że niektóre ciągi znaków są przekazywane w moim skrypcie powłoki i muszę go użyć, aby uruchomić zapytanie SQL:
W moim skrypcie muszę wykonać polecenie „WYBIERZ * Z tabeli, GDZIE nazwa IN („ aa ”,„ bb ”,„ cc ”,„ dd ”). Wtedy powyższe polecenie będzie przydatne.
źródło
printf -v bar ...
zamiast uruchamiać pętlę printf w podpowłoce i przechwytywać dane wyjściowe.Oto jeden, który obsługuje większość powłok kompatybilnych z POSIX:
źródło
local
).Działa również użycie pośredniej zmiennej do bezpośredniego odniesienia do tablicy. Można również użyć nazwanych referencji, ale stały się one dostępne dopiero w 4.3.
Zaletą korzystania z tej formy funkcji jest to, że separator może być opcjonalny (domyślnie jest to pierwszy znak domyślny
IFS
, który jest spacją; być może uczyni to pustym ciągiem, jeśli chcesz), i pozwala on uniknąć dwukrotnego zwiększania wartości (najpierw gdy przekazany jako parametry, a drugi jako"$@"
wewnątrz funkcji).To rozwiązanie nie wymaga również od użytkownika wywoływania funkcji w podstawieniu polecenia - które przywołuje podpowłokę, aby otrzymać połączoną wersję ciągu przypisanego do innej zmiennej.
Możesz użyć wygodniejszej nazwy dla tej funkcji.
Działa to od 3.1 do 5.0-alfa. Jak zaobserwowano, pośrednia zmienność działa nie tylko ze zmiennymi, ale także z innymi parametrami.
Tablice i elementy tablic są również parametrami (encje przechowujące wartość), a odniesienia do tablic są również technicznie odniesieniami do parametrów. I podobnie jak parametr specjalny
@
,array[@]
również stanowi ważne odniesienie.Zmienione lub selektywne formy rozszerzenia (takie jak rozszerzenie substringu), które odbiegają od odniesienia do samego parametru, już nie działają.
Aktualizacja
W wydanej wersji Bash 5.0 zmienna pośrednia jest już nazywana rozszerzeniem pośrednim, a jej zachowanie jest już wyraźnie udokumentowane w podręczniku:
Biorąc pod uwagę, że w dokumentacji
${parameter}
,parameter
jest określany jako „parametr powłoki, jak opisano (w) PARAMETRY lub odwołanie do tablicy ”. W dokumentacji tablic wspomniano, że „do dowolnego elementu tablicy można się odwoływać za pomocą${name[subscript]}
”. To sprawia,__r[@]
że odwołanie do tablicy.Dołącz według wersji argumentów
Zobacz mój komentarz w odpowiedzi Riccardo Galli .
źródło
__
jako nazwy zmiennej? Sprawia, że kod jest naprawdę nieczytelny.To podejście zajmuje się spacjami w obrębie wartości, ale wymaga pętli:
źródło
Jeśli budujesz tablicę w pętli, oto prosty sposób:
źródło
x=${"${arr[*]}"// /,}
To najkrótszy sposób na zrobienie tego.
Przykład,
źródło
bash: ${"${arr[*]}"// /,}: bad substitution
Być może późno na imprezę, ale to działa dla mnie:
źródło
Być może brakuje mi czegoś oczywistego, ponieważ jestem nowicjuszem w całym bash / zsh, ale wydaje mi się, że nie musisz go wcale używać
printf
. Bez tego nie robi się naprawdę brzydko.Przynajmniej do tej pory działało dla mnie bez problemu.
Na przykład,
join \| *.sh
co powiedzmy, że jestem w moim~
katalogu, generuje dane wyjścioweutilities.sh|play.sh|foobar.sh
. Wystarczająco dobrze dla mnie.EDYCJA: Jest to w zasadzie odpowiedź Nila Geisweillera , ale uogólniona na funkcję.
źródło
Daje to również dodatkowy przecinek na końcu. Nie jestem ekspertem od bash. Tylko mój 2c, ponieważ jest to bardziej elementarne i zrozumiałe
źródło
lub
źródło