Bash에서 배열의 요소를 어떻게 결합 할 수 있습니까?


416

Bash에 이와 같은 배열이있는 경우 :

FOO=( a b c )

요소를 쉼표로 어떻게 결합합니까? 예를 들어을 생성 a,b,c합니다.

답변:


571

Pascal Pilz의 솔루션을 100 % 순수 Bash의 함수로 다시 작성 (외부 명령 없음) :

function join_by { local IFS="$1"; shift; echo "$*"; }

예를 들어

join_by , a "b c" d #a,b c,d
join_by / var local tmp #var/local/tmp
join_by , "${FOO[@]}" #a,b,c

또는 @gniourf_gniourf의 아이디어를 사용하여 printf를 사용하여 다중 문자 구분 기호를 지원할 수 있습니다

function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; }

예를 들어

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

9
다중 문자 구분 기호에 이것을 사용하십시오. function join {perl -e '$ s = shift @ARGV; 인쇄 조인 ($ s, @ARGV); ' "$ @"; } ','abc # a, b, c 가입
Daniel Patru

4
어쨌든 @dpatru는 순수한 bash를 만들기 위해?
CMCDragonkai

4
@puchu 작동하지 않는 것은 다중 문자 구분 기호입니다. "공간이 작동하지 않는다"고 말하면 공간과의 결합이 작동하지 않는 것처럼 들립니다. 그렇습니다.
Eric

6
출력을 변수에 저장하는 경우 서브 쉘 생성을 촉진합니다. konsolebox스타일 사용 :) function join { local IFS=$1; __="${*:2}"; }또는 function join { IFS=$1 eval '__="${*:2}"'; }. 그런 다음 사용하십시오 __. 예, 나는 __결과 변수로 사용을 장려하는 사람입니다 .) (그리고 일반적인 반복 변수 또는 임시 변수). 컨셉이 인기있는 Bash 위키 사이트에 도착하면 나를 복사했습니다 :)
konsolebox

6
$d의 형식 지정자에 확장 을 넣지 마십시오 printf. 당신은 당신이 "탈출"때문에 당신은 이제 안전 생각 %하지만, 다른주의 사항이 있습니다 : 구분 기호가 백 슬래시 (예를 들어, 포함 된 경우 \n) 또는 때 하이픈 (그리고 내가 지금 생각할 수 없다, 아마 다른 사람)과의 구분이 시작됩니다. 물론이를 고칠 수 있습니다 (백 슬래시를 이중 백 슬래시로 대체하고 사용하십시오 printf -- "$d%s"). 그러나 어느 시점에서 쉘을 사용하는 대신 쉘과 싸우고 있다고 느낄 수 있습니다. 그렇기 때문에 아래 답변에서 가입 조건에 구분 기호를 추가했습니다.
gniourf_gniourf

206

또 다른 해결책 :

#!/bin/bash
foo=('foo bar' 'foo baz' 'bar baz')
bar=$(printf ",%s" "${foo[@]}")
bar=${bar:1}

echo $bar

편집 : 동일하지만 다중 문자 가변 길이 구분 기호의 경우 :

#!/bin/bash
separator=")|(" # e.g. constructing regex, pray it does not contain %s
foo=('foo bar' 'foo baz' 'bar baz')
regex="$( printf "${separator}%s" "${foo[@]}" )"
regex="${regex:${#separator}}" # remove leading separator
echo "${regex}"
# Prints: foo bar)|(foo baz)|(bar baz

7
+1. 무엇에 대해 printf -v bar ",%s" "${foo[@]}". fork(실제로 clone) 하나 적습니다 . 파일을 읽는 것도 잊어 버립니다 printf -v bar ",%s" $(<infile).
TrueY

14
기도에 $separator포함되어 있지 않거나 그 대신에 %s당신은 다음과 같이 printf강하게 할 수 있습니다 printf "%s%s" "$separator" "${foo[@]}".
musiphil

5
@musiphil 잘못되었습니다. bash man : "포맷은 모든 인수를 소비하는 데 필요에 따라 재사용됩니다. printf "%s%s"첫 번째 경우에만 출력 에서 두 개의 형식 플레이스 홀더를 사용하면 구분 기호를 사용한 다음 나머지 인수를 연결합니다.
AnyDev

3
@AndrDevEK : 실수를 잡아 주셔서 감사합니다. 대신 나는 다음과 같은 것을 제안 할 것이다 printf "%s" "${foo[@]/#/$separator}".
musiphil

2
@musiphil, 감사합니다. 예! 그런 다음 printf가 중복되고 해당 줄을로 줄일 수 있습니다 IFS=; regex="${foo[*]/#/$separator}". 이 시점에서 이것은 본질적으로 IMO가 처음부터 더 깨끗하다는, 즉 IFS 변경 범위와 임시 변수의 범위를 제한하는 기능을 사용하여 gniourf_gniourf의 대답이됩니다.
AnyDev

145
$ foo=(a "b c" d)
$ bar=$(IFS=, ; echo "${foo[*]}")
$ echo "$bar"
a,b c,d

3
콜론 주위의 큰 따옴표와 큰 따옴표는 필요하지 않습니다. 단지 내부 따옴표가 필요합니다 :bar=$( IFS=, ; echo "${foo[*]}" )
ceving

8
루프가 필요없고 외부 명령이 필요하지 않고 인수의 문자 세트에 추가 제한을 부과하지 않는 가장 컴팩트 한 솔루션의 경우 +1입니다.
sep

22
나는 해결책을 좋아하지만, IFS가 하나의 문자 인 경우에만 작동합니다
Jayen

8
@에서 *와 같이 대신 대신 사용하면 이것이 작동하지 않는 이유 는 $(IFS=, ; echo "${foo[@]}")무엇입니까? 나는 *이미 요소에 공백을 보존하고 있음을 알 수 있습니다 . 왜냐하면 @일반적 으로이 술에 필요하기 때문 입니다.
haridsv

10
위의 질문에 대한 답을 찾았습니다. 이에 대한 답은 IFS가에만 인정된다는 것입니다 *. bash 맨 페이지에서 "특수 매개 변수"를 검색하고 다음 옆에있는 설명을 찾으십시오 *.
haridsv

66

예를 들어

SAVE_IFS="$IFS"
IFS=","
FOOJOIN="${FOO[*]}"
IFS="$SAVE_IFS"

echo "$FOOJOIN"

3
그렇게하면 IFS-가 변수라고 생각합니다. 당신은해야 할 echo "-${IFS}-"(중괄호가 변수 이름에서 대시를 분리).
추후 공지가있을 때까지 일시 중지되었습니다.

1
여전히 같은 결과를 얻었습니다. (단지 요점을 설명하기 위해 대시를 넣었습니다… echo $IFS같은 일을합니다.
David Wolever

41
그러나 이것은 여전히 ​​작동하는 것 같습니다. 따라서 Bash의 대부분의 것들과 마찬가지로, 나는 그것을 이해하고 내 인생을 계속하는 것처럼 가장합니다.
David Wolever

2
"-"는 변수 이름에 유효한 문자가 아니므로 $ IFS-를 사용할 때 쉘이 올바른 작업을 수행하므로 $ {IFS}-(Linux 및 solaris의 bash, ksh, sh 및 zsh)가 필요하지 않습니다. 또한 동의합니다).
Idelic

2
@David 에코와 Dennis의 차이점은 그가 큰 따옴표를 사용했다는 것입니다. IFS의 내용은 단어 구분 문자의 선언으로 '입력시'사용되므로 항상 따옴표없이 빈 줄을 얻습니다.
martin clayton

30

놀랍게도 내 솔루션은 아직 제공되지 않았습니다. :) 이것은 가장 간단한 방법입니다. 기능이 필요하지 않습니다.

IFS=, eval 'joined="${foo[*]}"'

참고 :이 솔루션은 비 POSIX 모드에서 잘 작동하는 것으로 관찰되었습니다. 에서 POSIX 모드 , 요소는 여전히 제대로 결합되지만, IFS=,영구적된다.


불행히도 단일 문자 구분자에 대해서만 작동
maoizm

24

다음은 작업을 수행하는 100 % 순수 Bash 함수입니다.

join() {
    # $1 is return variable name
    # $2 is sep
    # $3... are the elements to join
    local retname=$1 sep=$2 ret=$3
    shift 3 || shift $(($#))
    printf -v "$retname" "%s" "$ret${@/#/$sep}"
}

보기:

$ a=( one two "three three" four five )
$ join joineda " and " "${a[@]}"
$ echo "$joineda"
one and two and three three and four and five
$ join joinedb randomsep "only one element"
$ echo "$joinedb"
only one element
$ join joinedc randomsep
$ echo "$joinedc"

$ a=( $' stuff with\nnewlines\n' $'and trailing newlines\n\n' )
$ join joineda $'a sep with\nnewlines\n' "${a[@]}"
$ echo "$joineda"
 stuff with
newlines
a sep with
newlines
and trailing newlines


$

이것은 후행 줄 바꿈도 유지하며 함수의 결과를 얻기 위해 서브 쉘이 필요하지 않습니다. printf -v(왜 원하지 않습니까?)가 마음에 들지 않고 변수 이름을 전달하면 반환 된 문자열에 전역 변수를 사용할 수 있습니다.

join() {
    # $1 is sep
    # $2... are the elements to join
    # return is in global variable join_ret
    local sep=$1 IFS=
    join_ret=$2
    shift 2 || shift $(($#))
    join_ret+="${*/#/$sep}"
}

1
마지막 해결책은 매우 좋지만 join_ret지역 변수를 만든 다음 끝에 에코하여 깨끗하게 만들 수 있습니다 . 이렇게하면 join ()을 일반적인 쉘 스크립팅 방식으로 사용할 수 있으며 $(join ":" one two three)전역 변수가 필요하지 않습니다.
James Sneeringer

1
@JamesSneeringer 서브 쉘을 피하기 위해 의도적으로이 디자인을 사용했습니다. 쉘 스크립팅에서는 다른 많은 언어와 달리 전역 변수가 그렇게 나쁜 것은 아닙니다. 특히 그들이 서브 쉘을 피하기 위해 여기에 있다면. 또한 $(...)후행 줄 바꿈을 제거합니다. 따라서 배열의 마지막 필드에 후행 줄 바꿈이 포함되어 있으면 줄 바꿈됩니다 (내 디자인으로 트리밍되지 않은 데모 참조).
gniourf_gniourf

이 ^ _ ^ 나를 행복하게하는, 멀티 문자 구분와 함께 작동
spiffytech

"printf -v를 원하지 않는 이유는 무엇입니까?"를 해결하려면 : Bash에서 로컬 변수는 실제로 함수 로컬이 아니므로 이와 같은 작업을 수행 할 수 있습니다. (로컬 변수 x를 사용하여 함수 f1을 호출하면 x를 수정하는 함수 f2가 호출됩니다. x는 f1의 범위 내에서 로컬로 선언됩니다) 그러나 실제로 로컬 변수가 작동하는 방식은 아닙니다. 로컬 변수가 실제로 로컬 인 경우 (또는 예를 들어 bash 및 ksh에서 모두 작동해야하는 스크립트에서 가정되는 경우)이 "전체 이름에 변수를 저장하여 값을 반환"스키마 전체에 문제가 발생합니다.
tetsujin

15

이것은 기존 솔루션과 크게 다르지 않지만 별도의 기능을 사용하지 않고 IFS부모 셸에서 수정하지 않으며 모두 한 줄에 있습니다.

arr=(a b c)
printf '%s\n' "$(IFS=,; printf '%s' "${arr[*]}")"

~를 야기하는

a,b,c

제한 : 구분 기호는 한 문자를 초과 할 수 없습니다.


13

외부 명령을 사용하지 않는 경우 :

$ FOO=( a b c )     # initialize the array
$ BAR=${FOO[@]}     # create a space delimited string from array
$ BAZ=${BAR// /,}   # use parameter expansion to substitute spaces with comma
$ echo $BAZ
a,b,c

경고, 요소에는 공백이 없다고 가정합니다.


4
중간 변수를 사용하지 않으려면 더 짧게 수행 할 수 있습니다.echo ${FOO[@]} | tr ' ' ','
jesjimher

2
나는 부정적인 투표를 이해하지 못한다. 여기에 게시 된 다른 것보다 훨씬 작고 읽기 쉬운 솔루션이며 공간이있을 때 작동하지 않는다는 것을 분명히 경고합니다.
jesjimher

12

배열을 문자열로 반향 한 다음 공백을 줄 바꿈으로 변환 한 다음 paste모든 줄을 한 줄로 결합 하는 데 사용 합니다.

tr " " "\n" <<< "$FOO" | paste -sd , -

결과 :

a,b,c

이것은 나에게 가장 빠르고 가장 깨끗한 것 같습니다!


$FOO그러나 배열의 첫 번째 요소입니다. 또한 공백이 포함 된 배열 요소도 중단됩니다.
Benjamin W.

9

@ doesn 's 's solution을 재사용하면 $ {: 1}의 정의와 중개 변수의 필요성을 피함으로써 하나의 진술만으로도 가능합니다.

echo $(printf "%s," "${LIST[@]}" | cut -d "," -f 1-${#LIST[@]} )

printf는 '포맷 문자열은 인수를 만족시키기 위해 필요한만큼 자주 재사용됩니다.' 문자열의 연결이 문서화되도록 매뉴얼 페이지에 있습니다. 그런 다음 LIST 길이를 사용하여 마지막 스퍼터를 자르는 것이 좋습니다. 컷은 필드 수로 LIST의 길이 만 유지하기 때문입니다.


7
s=$(IFS=, eval 'echo "${FOO[*]}"')

8
당신은 당신의 답을 살육시켜야합니다.
joce

가장 좋은 것. 감사!!
peter pan gz

4
이 답변이 보안 허점을 열어 요소의 공간을 파괴하기 때문에이 답변을 다운 투표 할 수 있기를 바랍니다.
eel ghEEz

1
실제로 @bxm은 공백을 유지하는 것으로 보이며 반향 인수 컨텍스트에서 벗어날 수 없습니다. 내가 생각하는 추가 @Q그들에 목공이있을 때 잘못 해석에서 결합 된 값을 벗어날 수 : foo=("a ," "b ' ' c" "' 'd e" "f " ";" "ls -latr"); s=$(IFS=, eval 'echo "${foo[*]@Q}"'); echo "${s}"출력'a ,','b '\'' '\'' c',''\'' '\''d e','f ',';','ls -latr '
장어 ghEEz

1
필요한 경우가 아니면 서브 쉘을 사용하는 솔루션을 피하십시오.
konsolebox

5

모든 길이의 구분 기호를 허용하는 printf 솔루션 (@ doesn 's는 중요하지 않습니다)

#/!bin/bash
foo=('foo bar' 'foo baz' 'bar baz')

sep=',' # can be of any length
bar=$(printf "${sep}%s" "${foo[@]}")
bar=${bar:${#sep}}

echo $bar

선행 쉼표로 출력을 생성합니다.
Mark Renouf

마지막 bar = $ {bar : $ {# sep}}은 구분 기호를 제거합니다. 방금 bash 쉘에 복사하여 붙여 넣었으며 작동합니다. 어떤 쉘을 사용하고 있습니까?
리카르도 갈리

2
모든 printf 형식 지정자 (예 : %s실수로 $sep문제가 발생할 수 있음)
Peter.O

sep로 소독 할 수 있습니다 ${sep//\%/%%}. 나는 귀하의 솔루션을 ${bar#${sep}}또는 ${bar%${sep}}(대체) 보다 낫습니다 . 결과를와 같은 일반 변수로 저장하는 함수로 변환하면 __좋지 않습니다 echo.
konsolebox

function join_by { printf -v __ "${1//\%/%%}%s" "${@:2}"; __=${__:${#1}}; }
konsolebox

4
$ set a 'b c' d

$ history -p "$@" | paste -sd,
a,b c,d

맨 위에 있어야합니다.
Eric Walker

6
이것은 정상에 있어서는 안됩니다. 만약 그렇다면 HISTSIZE=0?
har-wradim 12

@ har-wradim, 요령은 paste -sd, 역사의 사용에 것이 아닙니다.
Veda

@ Veda 아니요, 조합 사용에 관한 것이므로 HISTSIZE=0시도해보십시오.
har-wradim

4

최고 답변의 짧은 버전 :

joinStrings() { local a=("${@:3}"); printf "%s" "$2${a[@]/#/$1}"; }

용법:

joinStrings "$myDelimiter" "${myArray[@]}"

1
더 긴 버전이지만 배열 변수에 대한 인수 조각의 사본을 만들 필요는 없습니다.join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '%s' "${@/#/$d}"; }
Rockallite

또 다른 버전 : join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '$d%s' "${@}"; } 이것은 사용법과 함께 작동 join_strings 'delim' "${array[@]}"하거나 인용되지 않은 :join_strings 'delim' ${array[@]}
Cometsong

4

다음 아이디어와 함께 지금까지 모든 세계의 최고를 결합하십시오.

# join with separator
join_ws()  { local IFS=; local s="${*/#/$1}"; echo "${s#"$1$1$1"}"; }

이 작은 걸작은

  • 100 % 순수 bash (일시적으로 설정되지 않은 IFS로 매개 변수 확장, 외부 호출 없음, printf 없음 ...)
  • 작고 완벽하며 완벽한 (단일 및 다중 문자 제한 기, 공백, 줄 바꿈 및 기타 셸 특수 문자가 포함 된 제한 기, 빈 구분 기호 사용)
  • 효율적 (서브 쉘 없음, 배열 복사 없음)
  • 간단하고 어리 석고, 어느 정도는 아름답고 유익한

예 :

$ join_ws , a b c
a,b,c
$ join_ws '' a b c
abc
$ join_ws $'\n' a b c
a
b
c
$ join_ws ' \/ ' A B C
A \/ B \/ C

1
그렇게 좋지는 않습니다 : 적어도 2 가지 문제 : 1. join_ws ,(인수없이) 잘못 출력 ,,합니다. 2. join_ws , -e잘못 출력하지 않습니다. ( echo대신에 잘못 사용하고 있기 때문 printf입니다.) 난 당신의 사용을 광고하는 이유는 실제로 모르는 echo대신을 printf: echo악명 파괴하고, printf강력한 내장입니다.
gniourf_gniourf 21시 02 분

1

지금 나는 사용하고 있습니다 :

TO_IGNORE=(
    E201 # Whitespace after '('
    E301 # Expected N blank lines, found M
    E303 # Too many blank lines (pep8 gets confused by comments)
)
ARGS="--ignore `echo ${TO_IGNORE[@]} | tr ' ' ','`"

어느 것이 효과가 있지만 배열 요소에 공백이 있으면 (일반적인 경우) 끔찍하게 중단됩니다.

(관심있는 사람들을 위해, 이것은 pep8.py 주위의 래퍼 스크립트입니다 )


그 배열 값은 어디서 얻습니까? 그렇게 하드 코딩하고 있다면 왜 foo = "a, b, c"가 아닌가?
ghostdog74

이 경우 실제로 하고 값을 하드 코딩,하지만 난 개별적으로 각에 댓글을 달 수 있도록 배열에 넣고 싶습니다. 내가 의미하는 바를 보여주기 위해 답변을 업데이트했습니다.
David Wolever

실제로 bash를 사용한다고 가정하면 더 잘 작동 할 수 있습니다 ARGS="--ignore $(echo "${TO_IGNORE[@]}" | tr ' ' ',')". 연산자 $()는 백틱보다 강력합니다 ( $()및의 중첩 허용 ""). ${TO_IGNORE[@]}큰 따옴표로 감싸는 것도 도움이됩니다.
kevinarpe

1

내 시도.

$ array=(one two "three four" five)
$ echo "${array[0]}$(printf " SEP %s" "${array[@]:1}")"
one SEP two SEP three four SEP five

1

여러 문자 구분 기호에 perl을 사용하십시오.

function join {
   perl -e '$s = shift @ARGV; print join($s, @ARGV);' "$@"; 
}

join ', ' a b c # a, b, c

또는 한 줄로 :

perl -le 'print join(shift, @ARGV);' ', ' 1 2 3
1, 2, 3

있지만, 나를 위해 작동 join에 약간의 쓰레기와 이름이 충돌 OS X.. 내가 호출 것 conjoined, 아마, 또는 jackie_joyner_kersee?
Alex Gray

1

지금까지 최고의 세계 조합에 대한 자세한 의견은 @gniourf_gniourf에게 감사드립니다. 철저하게 설계 및 테스트되지 않은 코드를 게시하여 죄송합니다. 더 나은 시도가 있습니다.

# join with separator
join_ws() { local d=$1 s=$2; shift 2 && printf %s "$s${@/#/$d}"; }

개념에 의한이 아름다움은

  • (아직도) 100 % 순수 bash (printf도 내장되어 있음을 명시 적으로 지적 해 주셔서 감사합니다. 전에는 알지 못했습니다 ...)
  • 다중 문자 분리 문자와 함께 작동
  • 보다 간결하고 완전하며 이번에는 쉘 특수 문자 또는 제어 문자 사용 또는 구분 기호 및 / 또는 매개 변수에 문자 없음 및 엣지 케이스를 포함하여 쉘 스크립트의 임의의 하위 문자열로 신중하고 장기적인 스트레스 테스트를 거쳤습니다. , 코너 케이스 및 인수가 전혀없는 다른 퀴즈. 그렇다고 더 이상 버그가 없다고 보장 할 수는 없지만 버그를 찾기가 조금 더 어려울 것입니다. BTW, 현재 최고 투표 답변과 관련 문제조차도 -e 버그 ...

추가 예 :

$ join_ws '' a b c
abc
$ join_ws ':' {1,7}{A..C}
1A:1B:1C:7A:7B:7C
$ join_ws -e -e
-e
$ join_ws $'\033[F' $'\n\n\n'  1.  2.  3.  $'\n\n\n\n'
3.
2.
1.
$ join_ws $ 
$

1

결합하려는 요소가 공백으로 구분 된 문자열 배열이 아닌 경우 다음과 같이 할 수 있습니다.

foo="aa bb cc dd"
bar=`for i in $foo; do printf ",'%s'" $i; done`
bar=${bar:1}
echo $bar
    'aa','bb','cc','dd'

예를 들어, 유스 케이스는 일부 문자열이 쉘 스크립트로 전달되며 SQL 쿼리에서 실행하려면 이것을 사용해야합니다.

./my_script "aa bb cc dd"

my_script에서 "SELECT * FROM table WHERE name IN ( 'aa', 'bb', 'cc', 'dd')을 수행해야합니다. 그러면 위의 명령이 유용합니다.


printf -v bar ...서브 쉘에서 printf 루프를 실행하고 출력을 캡처하는 대신 사용할 수 있습니다 .
codeforester

위의 모든 화려한 upvoted 솔루션은 작동하지 않았지만 당신의 원유는 나를 위해했습니다 (Y)
ishandutta2007

1

다음은 대부분의 POSIX 호환 쉘이 지원하는 것입니다.

join_by() {
    # Usage:  join_by "||" a b c d
    local arg arr=() sep="$1"
    shift
    for arg in "$@"; do
        if [ 0 -lt "${#arr[@]}" ]; then
            arr+=("${sep}")
        fi
        arr+=("${arg}") || break
    done
    printf "%s" "${arr[@]}"
}

Bash 코드는 좋지만 POSIX 에는 배열 (또는 local)이 전혀 없습니다.
Anders Kaseorg

@Anders : 네 나는 아주 최근이 어려운 방법을 배웠다 :( 대부분의 POSIX 호환 쉘이 배열을 지원하는 것 않기 때문에 비록 내가 지금은 그것을 떠날거야.
user541686

1

변수 간접 참조를 사용하여 배열을 직접 참조해도 작동합니다. 명명 된 참조도 사용할 수 있지만 4.3에서만 사용할 수있게되었습니다.

이 형식의 함수를 사용하면 구분 기호를 선택적으로 사용할 수 있으며 (기본값 IFS은 공백 의 첫 문자 인 기본값입니다 . 원한다면 빈 문자열로 만들 수도 있습니다) 값을 두 번 확장하지 않습니다 (먼저 매개 변수로 전달 될 때 두 번째로"$@" 함수 내부에서 ).

이 솔루션은 또한 사용자가 다른 변수에 할당 된 문자열의 결합 된 버전을 얻기 위해 서브 쉘을 소환하는 명령 대체 내에서 함수를 호출 할 필요가 없습니다.

function join_by_ref {
    __=
    local __r=$1[@] __s=${2-' '}
    printf -v __ "${__s//\%/%%}%s" "${!__r}"
    __=${__:${#__s}}
}

array=(1 2 3 4)

join_by_ref array
echo "$__" # Prints '1 2 3 4'.

join_by_ref array '%s'
echo "$__" # Prints '1%s2%s3%s4'.

join_by_ref 'invalid*' '%s' # Bash 4.4 shows "invalid*[@]: bad substitution".
echo "$__" # Prints nothing but newline.

보다 편리한 기능 명을 사용하십시오.

이것은 3.1에서 5.0- 알파까지 작동합니다. 관찰 된 바와 같이, 변수 간접은 변수뿐만 아니라 다른 매개 변수와도 작동합니다.

매개 변수는 값을 저장하는 엔티티입니다. 이름, 숫자 또는 특수 매개 변수 아래 아래에 나열된 특수 문자 중 하나 일 수 있습니다. 변수는 이름으로 표시되는 매개 변수입니다.

배열과 배열 요소도 매개 변수 (값을 저장하는 엔티티)이며 배열에 대한 참조는 기술적으로 매개 변수에 대한 참조입니다. 그리고 특별한 매개 변수 @array[@]마찬가지로 유효한 참조를 만듭니다.

매개 변수 자체에서 참조를 벗어난 변경되거나 선택적인 확장 형식 (예 : 하위 문자열 확장)이 더 이상 작동하지 않습니다.

최신 정보

Bash 5.0 릴리스 버전에서 변수 간접 지정 은 이미 간접 확장 이라고 하며 그 동작은 매뉴얼에 명시 적으로 문서화되어 있습니다.

매개 변수의 첫 문자가 느낌표 (!)이고 매개 변수가 이름 참조가 아닌 경우 간접적 인 수준을 나타냅니다. Bash는 나머지 매개 변수를 새 매개 변수로 확장하여 형성된 값을 사용합니다. 그런 다음이 값이 확장되고 해당 값은 원래 매개 변수의 확장이 아닌 나머지 확장에 사용됩니다. 이것을 간접 확장이라고합니다.

의 문서에 참고 촬영 ${parameter}, parameter라고 "설명 (단위)로서 쉘 파라미터 PARAMETERS 또는 배열을 참조 ." 그리고 배열 문서에서 "배열의 모든 요소는 ${name[subscript]}"를 사용하여 참조 될 수 있습니다 . 이것은 __r[@]배열 참조를 만듭니다 .

인수 버전으로 결합

Riccardo Galli 's answer의견 을 참조하십시오 .


2
__변수 이름 으로 사용해야하는 특별한 이유가 있습니까? 코드를 실제로 읽을 수 없게 만듭니다.
PesaThe

@PesaThe 단지 환경 ​​설정입니다. 반환 변수에 일반 이름을 사용하는 것이 좋습니다. 제네릭 이외의 다른 이름은 특정 기능에 속하므로 암기해야합니다. 다른 변수에서 값을 반환하는 여러 함수를 호출하면 코드를 쉽게 따르지 않을 수 있습니다. 일반 이름을 사용하면 스크립터가 충돌을 피하기 위해 반환 변수에서 적절한 변수로 값을 전송하게되며, 반환 된 값의 위치가 명확 해 지므로 코드를 더 읽기 쉽게 만듭니다. 그래도 그 규칙에 대한 예외는 거의 없습니다.
konsolebox

0

이 방법은 값 내의 공백을 처리하지만 루프가 필요합니다.

#!/bin/bash

FOO=( a b c )
BAR=""

for index in ${!FOO[*]}
do
    BAR="$BAR,${FOO[$index]}"
done
echo ${BAR:1}

0

루프에서 배열을 빌드하는 경우 간단한 방법은 다음과 같습니다.

arr=()
for x in $(some_cmd); do
   arr+=($x,)
done
arr[-1]=${arr[-1]%,}
echo ${arr[*]}

0

x=${"${arr[*]}"// /,}

가장 짧은 방법입니다.

예,

arr=(1 2 3 4 5)
x=${"${arr[*]}"// /,}
echo $x  # output: 1,2,3,4,5

1
공백이있는 문자열에는 제대로 작동하지 않습니다 .`t = (a "b c"d); echo $ {t [2]} ( "b c"로 인쇄); echo $ { "$ {t [*]}"// /,} (인쇄 a, b, c, d)
kounoupis

7
bash: ${"${arr[*]}"// /,}: bad substitution
카메론 허드슨

0

아마도 파티에 늦었을 수도 있지만 이것은 나를 위해 작동합니다.

function joinArray() {
  local delimiter="${1}"
  local output="${2}"
  for param in ${@:3}; do
    output="${output}${delimiter}${param}"
  done

  echo "${output}"
}

-1

아마도 나는 bash / zsh 전체에 익숙하지 않기 때문에 분명한 것을 놓치고 있지만 전혀 사용할 필요가없는 것처럼 보입니다 printf. 그것 없이는 정말 못 생겼습니다.

join() {
  separator=$1
  arr=$*
  arr=${arr:2} # throw away separator and following space
  arr=${arr// /$separator}
}

적어도 지금까지 문제없이 작동했습니다.

예를 들어, join \| *.sh~디렉토리 에 있다고 가정 해 보겠습니다.utilities.sh|play.sh|foobar.sh . 나를 위해 충분합니다.

편집 : 이것은 기본적으로 Nil Geisweiller의 답변 이지만 기능으로 일반화되었습니다.


1
나는 downvoter는 아니지만 함수에서 전역을 조작하는 것은 꽤 별난 것 같습니다.
tripleee

-2
liststr=""
for item in list
do
    liststr=$item,$liststr
done
LEN=`expr length $liststr`
LEN=`expr $LEN - 1`
liststr=${liststr:0:$LEN}

이것은 끝에 추가 쉼표를 처리합니다. 나는 배쉬 전문가가 아니다. 이 2c는 더 기초적이고 이해하기 쉽기 때문에


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

또는

$ a=(1 "a b" 3)
$ b=$(IFS=, ; echo "${a[*]}")
$ echo $b
1,a b,3
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.