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
'Programming' 카테고리의 다른 글
SQL Server에서 상위 100 개의 레코드를 업데이트하는 방법 (0) | 2020.02.26 |
---|---|
단위 테스트와 기능 테스트 (0) | 2020.02.26 |
마이크로 초 구성 요소없이 문자열로 파이썬 날짜 시간 (0) | 2020.02.26 |
문자열에서 모든 줄 바꿈을 제거하는 방법 (0) | 2020.02.26 |
JavaScript의 For..In 루프-키 값 쌍 (0) | 2020.02.26 |