Bash 배열의 요소를 구분된 문자열로 결합하려면 어떻게 해야 합니까?
Bash에 이와 같은 배열이 있는 경우:
FOO=( a b c )
요소를 쉼표로 결합하려면 어떻게 해야 합니까?를 들어, 를들어, 산생을 a,b,c
.
다중 문자 구분 기호를 지원하는 100% 순수 Bash 함수는 다음과 같습니다.
function join_by {
local d=${1-} f=${2-}
if shift 2; then
printf %s "$f" "${@/#/$d}"
fi
}
예를들면,
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
join_by '-n' '-e' '-E' '-n' #-e-n-E-n-n
join_by , #
join_by , a #a
@gniourf_gniourf, @AdamKatz, @MattCowell @x-yuri 아이기합반의니로으다를어디. 옵과함작동다니합께와 함께 작동합니다.errexit
(set -e
및 ) 및nounset
(set -u
).
또는 단일 문자 구분 기호만 지원하는 더 간단한 함수는 다음과 같습니다.
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
이 솔루션은 Pascal Pilz의 원래 제안을 기반으로 합니다.
여기서 이전에 제안한 솔루션에 대한 자세한 설명은 meleu의 dev.to 기사인 "how to join array elements in bash script"에서 확인할 수 있습니다.
또 다른 솔루션:
#!/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"
외부 명령 사용 안 함:
$ 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
경고. 요소에 공백이 없는 것으로 가정합니다.
이 간단한 단일 문자 구분 기호 솔루션에는 비 POSIX 모드가 필요합니다.POSIX 모드에서는 요소가 여전히 올바르게 결합되지만,IFS=,
할당이 영구적으로 됩니다.
IFS=, eval 'joined="${foo[*]}"'
으로 입니다.#!bash
으로 비 비모드에서 "resisted"를 하십시오.set +o posix
또는shopt -uo posix
대본의 첫머리에.
다중 문자 구분 기호의 경우 다음을 사용하는 것이 좋습니다.printf
탈출 및 인덱싱 기술을 갖춘 솔루션입니다.
function join {
local __sep=${2-} __temp
printf -v __temp "${__sep//%/%%}%s" "${@:3}"
printf -v "$1" %s "${__temp:${#__sep}}"
}
join joined ', ' "${foo[@]}"
또는
function join {
printf -v __ "${1//%/%%}%s" "${@:2}"
__=${__:${#1}}
}
join ', ' "${foo[@]}"
joined=$__
이것은 Riccardo Galli의 답변에 근거하여 나의 제안이 적용되었습니다.
솔루션과 하는 것을 피하고,.IFS
상위 셸에 있으며 모두 한 줄로 되어 있습니다.
arr=(a b c)
printf '%s\n' "$(IFS=,; printf '%s' "${arr[*]}")"
결과적으로
a,b,c
제한: 구분 기호는 한 문자를 초과할 수 없습니다.
이는 단순화될 수 있습니다.
(IFS=,; printf '%s' "${arr[*]}")
그 시점에서 그것은 기본적으로 파스칼의 대답과 같지만, 사용합니다.printf
에 echo
결과를 변수에 할당하는 대신 stdout으로 출력합니다.
이 기능을 수행하는 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
이것이 저에게 가장 빠르고 깨끗한 것 같습니다!
@의 재사용은 문제가 되지 않지만 ${:1} 대체와 중간 변수의 필요성을 피함으로써 하나의 진술로 해결할 수 있습니다.
echo $(printf "%s," "${LIST[@]}" | cut -d "," -f 1-${#LIST[@]} )
printf의 man 페이지에는 문자열의 연결이 문서화되도록 '형식 문자열은 인수를 충족하기 위해 필요한 만큼 자주 재사용됩니다.'가 있습니다.그런 다음 LIST 길이를 사용하여 마지막 스퍼레이터를 자릅니다. 자르기는 필드 수가 셀 때 LIST의 길이만 유지하기 때문입니다.
x=${arr[*]// /,}
이것이 그것을 하는 가장 짧은 방법입니다.
예,
# ZSH:
arr=(1 "2 3" 4 5)
x=${"${arr[*]}"// /,}
echo $x # output: 1,2,3,4,5
# ZSH/BASH:
arr=(1 "2 3" 4 5)
a=${arr[*]}
x=${a// /,}
echo $x # output: 1,2,3,4,5
모든 길이의 구분 기호를 허용하는 printf 솔루션(@prett'matters 답변 기준)
#/!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
s=$(IFS=, eval 'echo "${FOO[*]}"')
상위 답변의 단축 버전:
joinStrings() { local a=("${@:3}"); printf "%s" "$2${a[@]/#/$1}"; }
용도:
joinStrings "$myDelimiter" "${myArray[@]}"
@gniourf_gniourf에 감사드립니다. 지금까지 저의 최고의 세계 조합에 대한 자세한 의견을 들려주셔서 감사합니다.철저하게 설계 및 테스트되지 않은 코드를 게시하여 죄송합니다.여기 더 좋은 시도가 있습니다.
# join with separator
join_ws() { local d=$1 s=$2; shift 2 && printf %s "$s${@/#/$d}"; }
잉태에 의한 이 아름다움은
- (그래도) 100% 순수 bash (프린트f도 기본 제공됨을 명시적으로 지적해주셔서 감사합니다.전에는 이런 사실을 몰랐어요...)
- 다중 문자 구분 기호가 있는 작업
- 더 작고 더 완전하며, 이번에는 셸 스크립트의 무작위 하위 문자열을 사용하여 신중하게 장기적인 스트레스를 고려하고, 셸 특수 문자 또는 제어 문자의 사용을 포함하거나 구분자 및/또는 매개 변수, 에지 케이스, 모서리 케이스 및 인수가 전혀 없는 기타 문제를 포함합니다.그렇다고 해서 더 이상 버그가 없다는 보장은 없지만, 버그를 찾는 것은 조금 더 어려울 것입니다.그나저나, 심지어 현재 상위 투표된 답변과 관련된 것들조차도 - ebug...와 같은 것들로 고통 받고 있습니다.
추가 예:
$ 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 $
$
$ set a 'b c' d
$ history -p "$@" | paste -sd,
a,b c,d
지금까지의 최고의 아이디어를 다음과 같이 결합합니다.
# 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
다음은 약간 이상하지만 다중 문자 구분 기호에 적합하고 모든 값(공백 또는 기타 값 포함)을 지원하는 단일 라이너입니다.
ar=(abc "foo bar" 456)
delim=" | "
printf "%s\n$delim\n" "${ar[@]}" | head -n-1 | paste -sd ''
콘솔에 다음과 같이 표시됩니다.
abc | foo bar | 456
참고: 일부 솔루션의 사용 방법에 주목printf
와 함께${ar[*]}
는 리고몇몇은그은몇.${ar[@]}
?
있는 사람들은@
을 사용합니다.printf
형식 템플릿을 반복하여 여러 인수를 지원하는 기능입니다.
있는 사람들은*
사용하면 안 됩니다.실제로 필요하지 않습니다.printf
필드 구분자와 bash의 단어 확장을 조작하는 것에 의존합니다.이것들도 마찬가지로 작동할 것입니다.echo
,cat
기타 - 이 솔루션들은 사용할 가능성이 높습니다.printf
저자는 그들이 하는 일을 제대로 이해하지 못하기 때문에...
나의 시도.
$ array=(one two "three four" five)
$ echo "${array[0]}$(printf " SEP %s" "${array[@]:1}")"
one SEP two SEP three four SEP five
저는 이것이 베너민 W.가 이미 언급했듯이 가장 짧은 해결책이라고 믿습니다.
(IFS=,; printf %s "${a[*]}")
zsh를 사용하면 하위 셸을 삭제할 수 있다는 점을 추가하고 싶었습니다.
IFS=, printf %s "${a[*]}"
테스트:
a=(1 'a b' 3)
IFS=, printf %s "${a[*]}"
1,a b,3
현재 사용 중인 항목:
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 주변의 래퍼 스크립트입니다.)
다중 문자 구분 기호에 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
배열을 루프로 구성하는 경우 간단한 방법은 다음과 같습니다.
arr=()
for x in $(some_cmd); do
arr+=($x,)
done
arr[-1]=${arr[-1]%,}
echo ${arr[*]}
조인할 요소가 단순히 공백으로 구분된 문자열이 아닌 경우 다음과 같은 작업을 수행할 수 있습니다.
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')"을 수행해야 합니다.그러면 위의 명령이 유용할 것입니다.
변수를 사용하여 배열을 직접 참조하는 것도 효과적입니다.명명된 참조도 사용할 수 있지만 4.3에서만 사용할 수 있게 되었습니다.
할 수 은 " 이형의함사기구면용하기분선호사로를다으택용있수적니장문자습첫할있점번이는다째기의값본본은값식수를기▁the▁character▁of▁first▁optional▁default▁the▁is(첫문번자▁ofdefault▁separator▁functions▁form▁(다▁this▁advantage▁a▁the▁to▁that째니▁have있▁you습이▁using").IFS
빈 수 , 번 번째는 변수로 전달할 때, 두 번째는 이다니공입간는다니▁as공).). 원한다면 빈 문자열로 만들 수 있습니다. 그리고 값을 두 번 확장하는 것을 방지합니다(첫 번째는 매개 변수로 전달될 때, 두 번째는"$@"
함수 내부).
또한 이 솔루션은 사용자가 다른 변수에 할당된 문자열의 조인된 버전을 얻기 위해 하위 셸을 호출하는 명령 대체 내부의 함수를 호출할 필요가 없습니다.
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 릴리스 버전에서 방향 변수는 이미 간접 확장이라고 하며 그 동작은 이미 설명서에 명시되어 있습니다.
매개 변수의 첫 번째 문자가 느낌표(!)이고 매개 변수가 nameref가 아닌 경우 방향 수준이 도입됩니다.Bash는 나머지 매개 변수를 확장하여 형성된 값을 새 매개 변수로 사용합니다. 그러면 이 값이 확장되고 원래 매개 변수의 확장이 아닌 나머지 확장에서 사용됩니다.이를 간접 확장이라고 합니다.
다음 문서에서 언급한 바와 같이,${parameter}
,parameter
"PARAMETERS 또는 배열 참조에 설명된 셸 매개 변수"라고 합니다.그리고 배열의 설명서에서 "배열의 모든 요소는 "를 사용하여 참조할 수 있습니다.이것은 만듭니다.__r[@]
배열 참조
인수별 가입 버전
Riccardo Galli의 답변에서 제 의견을 참조하십시오.
파티에 늦었을 수도 있지만, 이것은 나에게 효과가 있습니다.
function joinArray() {
local delimiter="${1}"
local output="${2}"
for param in ${@:3}; do
output="${output}${delimiter}${param}"
done
echo "${output}"
}
대부분은 아니지만 이러한 솔루션의 대부분은 난해한 구문, 엄청난 정규식 트릭 또는 외부 실행 파일에 대한 호출에 의존합니다.저는 이해하기 쉽고 성능 면에서 약간 차선일 뿐인 단순한 bash 전용 솔루션을 제안하고자 합니다.
join_by () {
# Argument #1 is the separator. It can be multi-character.
# Argument #2, 3, and so on, are the elements to be joined.
# Usage: join_by ", " "${array[@]}"
local SEPARATOR="$1"
shift
local F=0
for x in "$@"
do
if [[ F -eq 1 ]]
then
echo -n "$SEPARATOR"
else
F=1
fi
echo -n "$x"
done
echo
}
예:
$ a=( 1 "2 2" 3 )
$ join_by ", " "${a[@]}"
1, 2 2, 3
$
제가 지적하고 싶은 것은 어떤 해결책이든/usr/bin/[
또는/usr/bin/printf
100% 순수 bash를 사용하기 때문에 본질적으로 솔루션보다 느립니다.성능의 예로, 여기 1,000,000개의 임의 정수로 배열을 만든 다음, 모든 정수를 쉼표로 결합하고 시간을 지정하는 데모가 있습니다.
$ eval $(echo -n "a=("; x=0 ; while [[ x -lt 1000000 ]]; do echo -n " $RANDOM" ; x=$((x+1)); done; echo " )")
$ time join_by , ${a[@]} >/dev/null
real 0m8.590s
user 0m8.591s
sys 0m0.000s
$
이 제품은 특히 다음 제품과 호환됩니다.busybox
의sh
그리고.$@
:
$ FOO=(a b c)
$ printf '%s\n' "${FOO[@]}" | paste -sd,
a,b,c
또는:
join_by() {
local d=$1
shift
printf '%s\n' "$@" | paste -sd "$d"
}
join_by , "${FOO[@]}" # a,b,c
이 접근 방식은 값 내의 공백을 처리하지만 루프가 필요합니다.
#!/bin/bash
FOO=( a b c )
BAR=""
for index in ${!FOO[*]}
do
BAR="$BAR,${FOO[$index]}"
done
echo ${BAR: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
좋습니다.내게는 충분히 좋았다.
편집: 이것은 기본적으로 닐 가이스와일러의 대답이지만, 함수로 일반화되어 있습니다.
언급URL : https://stackoverflow.com/questions/1527049/how-can-i-join-elements-of-a-bash-array-into-a-delimited-string
'programing' 카테고리의 다른 글
Zure 웹 앱 배포 후 건설 중인 사이트 메시지를 가져오는 중 (0) | 2023.04.27 |
---|---|
HTML 중첩 목록을 만드는 올바른 방법은 무엇입니까? (0) | 2023.04.27 |
TSQL을 사용하여 데이터베이스의 SQL Server 버전을 어떻게 확인합니까? (0) | 2023.04.27 |
Swift 3에서 지연을 프로그래밍하는 방법 (0) | 2023.04.27 |
아이폰 시뮬레이터가 갑자기 매우 느리게 실행되기 시작했습니다. (0) | 2023.04.27 |