Programming

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

procodes 2020. 2. 26. 22:46
반응형

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


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

FOO=( a b c )

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


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

또 다른 해결책 :

#!/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

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

예를 들어

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

echo "$FOOJOIN"

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

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

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


다음은 작업을 수행하는 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}"
}

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

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

결과 :

a,b,c

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


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

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

~를 야기하는

a,b,c

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


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

$ 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

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


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

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

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


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

모든 길이의 구분 기호를 허용하는 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

$ set a 'b c' d

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

최고 답변의 짧은 버전 :

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

용법:

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

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

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

이 작은 걸작은

  • 100 % 순수 배쉬
  • 작고 완벽하며 완벽한 (단일 및 다중 문자 제한 기, 공백, 줄 바꿈 및 기타 셸 특수 문자가 포함 된 제한 기, 빈 구분 기호 사용)
  • 효율적 (서브 쉘 없음, 배열 복사 없음)
  • 간단하고 어리 석고, 어느 정도는 아름답고 유익한

예 :

$ 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

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

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 주위의 래퍼 스크립트입니다 )


내 시도.

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

여러 문자 구분 기호에 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

지금까지 최고의 세계 조합에 대한 자세한 의견은 @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 $ 
$

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

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')을 수행해야합니다. 그러면 위의 명령이 유용합니다.


다음은 대부분의 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[@]}"
}

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

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

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

단점은 올바른 매개 변수 이름을 전달하는 데주의를 기울여야하며 전달 __r하면 __r[@]. 다른 형식의 매개 변수를 확장하기위한 변수 간접 동작도 명시 적으로 문서화되어 있지 않습니다.

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[@]마찬가지로 유효한 참조를 만듭니다.

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


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

#!/bin/bash

FOO=( a b c )
BAR=""

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

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

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

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

가장 짧은 방법입니다.

예,

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

아마도 나는 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의 답변 이지만 기능으로 일반화되었습니다.


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

이것은 끝에 추가 쉼표를 처리합니다. 나는 배쉬 전문가가 아니다. 이 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

참고 URL : https://stackoverflow.com/questions/1527049/how-can-i-join-elements-of-an-array-in-bash



반응형