답변:
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
konsolebox
스타일 사용 :) function join { local IFS=$1; __="${*:2}"; }
또는 function join { IFS=$1 eval '__="${*:2}"'; }
. 그런 다음 사용하십시오 __
. 예, 나는 __
결과 변수로 사용을 장려하는 사람입니다 .) (그리고 일반적인 반복 변수 또는 임시 변수). 컨셉이 인기있는 Bash 위키 사이트에 도착하면 나를 복사했습니다 :)
$d
의 형식 지정자에 확장 을 넣지 마십시오 printf
. 당신은 당신이 "탈출"때문에 당신은 이제 안전 생각 %
하지만, 다른주의 사항이 있습니다 : 구분 기호가 백 슬래시 (예를 들어, 포함 된 경우 \n
) 또는 때 하이픈 (그리고 내가 지금 생각할 수 없다, 아마 다른 사람)과의 구분이 시작됩니다. 물론이를 고칠 수 있습니다 (백 슬래시를 이중 백 슬래시로 대체하고 사용하십시오 printf -- "$d%s"
). 그러나 어느 시점에서 쉘을 사용하는 대신 쉘과 싸우고 있다고 느낄 수 있습니다. 그렇기 때문에 아래 답변에서 가입 조건에 구분 기호를 추가했습니다.
또 다른 해결책 :
#!/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
printf -v bar ",%s" "${foo[@]}"
. fork
(실제로 clone
) 하나 적습니다 . 파일을 읽는 것도 잊어 버립니다 printf -v bar ",%s" $(<infile)
.
$separator
포함되어 있지 않거나 그 대신에 %s
당신은 다음과 같이 printf
강하게 할 수 있습니다 printf "%s%s" "$separator" "${foo[@]}"
.
printf "%s%s"
첫 번째 경우에만 출력 에서 두 개의 형식 플레이스 홀더를 사용하면 구분 기호를 사용한 다음 나머지 인수를 연결합니다.
printf "%s" "${foo[@]/#/$separator}"
.
IFS=; regex="${foo[*]/#/$separator}"
. 이 시점에서 이것은 본질적으로 IMO가 처음부터 더 깨끗하다는, 즉 IFS 변경 범위와 임시 변수의 범위를 제한하는 기능을 사용하여 gniourf_gniourf의 대답이됩니다.
$ foo=(a "b c" d)
$ bar=$(IFS=, ; echo "${foo[*]}")
$ echo "$bar"
a,b c,d
bar=$( IFS=, ; echo "${foo[*]}" )
@
에서 *
와 같이 대신 대신 사용하면 이것이 작동하지 않는 이유 는 $(IFS=, ; echo "${foo[@]}")
무엇입니까? 나는 *
이미 요소에 공백을 보존하고 있음을 알 수 있습니다 . 왜냐하면 @
일반적 으로이 술에 필요하기 때문 입니다.
*
. bash 맨 페이지에서 "특수 매개 변수"를 검색하고 다음 옆에있는 설명을 찾으십시오 *
.
예를 들어
SAVE_IFS="$IFS"
IFS=","
FOOJOIN="${FOO[*]}"
IFS="$SAVE_IFS"
echo "$FOOJOIN"
echo "-${IFS}-"
(중괄호가 변수 이름에서 대시를 분리).
echo $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}"
}
join_ret
지역 변수를 만든 다음 끝에 에코하여 깨끗하게 만들 수 있습니다 . 이렇게하면 join ()을 일반적인 쉘 스크립팅 방식으로 사용할 수 있으며 $(join ":" one two three)
전역 변수가 필요하지 않습니다.
$(...)
후행 줄 바꿈을 제거합니다. 따라서 배열의 마지막 필드에 후행 줄 바꿈이 포함되어 있으면 줄 바꿈됩니다 (내 디자인으로 트리밍되지 않은 데모 참조).
외부 명령을 사용하지 않는 경우 :
$ 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
경고, 요소에는 공백이 없다고 가정합니다.
echo ${FOO[@]} | tr ' ' ','
배열을 문자열로 반향 한 다음 공백을 줄 바꿈으로 변환 한 다음 paste
모든 줄을 한 줄로 결합 하는 데 사용 합니다.
tr " " "\n" <<< "$FOO" | paste -sd , -
결과 :
a,b,c
이것은 나에게 가장 빠르고 가장 깨끗한 것 같습니다!
$FOO
그러나 배열의 첫 번째 요소입니다. 또한 공백이 포함 된 배열 요소도 중단됩니다.
@ doesn 's 's solution을 재사용하면 $ {: 1}의 정의와 중개 변수의 필요성을 피함으로써 하나의 진술만으로도 가능합니다.
echo $(printf "%s," "${LIST[@]}" | cut -d "," -f 1-${#LIST[@]} )
printf는 '포맷 문자열은 인수를 만족시키기 위해 필요한만큼 자주 재사용됩니다.' 문자열의 연결이 문서화되도록 매뉴얼 페이지에 있습니다. 그런 다음 LIST 길이를 사용하여 마지막 스퍼터를 자르는 것이 좋습니다. 컷은 필드 수로 LIST의 길이 만 유지하기 때문입니다.
s=$(IFS=, eval 'echo "${FOO[*]}"')
@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 '
모든 길이의 구분 기호를 허용하는 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
printf
형식 지정자 (예 : %s
실수로 $sep
문제가 발생할 수 있음)
sep
로 소독 할 수 있습니다 ${sep//\%/%%}
. 나는 귀하의 솔루션을 ${bar#${sep}}
또는 ${bar%${sep}}
(대체) 보다 낫습니다 . 결과를와 같은 일반 변수로 저장하는 함수로 변환하면 __
좋지 않습니다 echo
.
function join_by { printf -v __ "${1//\%/%%}%s" "${@:2}"; __=${__:${#1}}; }
$ set a 'b c' d
$ history -p "$@" | paste -sd,
a,b c,d
HISTSIZE=0
?
paste -sd,
역사의 사용에 것이 아닙니다.
HISTSIZE=0
시도해보십시오.
최고 답변의 짧은 버전 :
joinStrings() { local a=("${@:3}"); printf "%s" "$2${a[@]/#/$1}"; }
용법:
joinStrings "$myDelimiter" "${myArray[@]}"
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' "${@}"; }
이것은 사용법과 함께 작동 join_strings 'delim' "${array[@]}"
하거나 인용되지 않은 :join_strings 'delim' ${array[@]}
다음 아이디어와 함께 지금까지 모든 세계의 최고를 결합하십시오.
# join with separator
join_ws() { local IFS=; local s="${*/#/$1}"; echo "${s#"$1$1$1"}"; }
이 작은 걸작은
예 :
$ 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
join_ws ,
(인수없이) 잘못 출력 ,,
합니다. 2. join_ws , -e
잘못 출력하지 않습니다. ( echo
대신에 잘못 사용하고 있기 때문 printf
입니다.) 난 당신의 사용을 광고하는 이유는 실제로 모르는 echo
대신을 printf
: echo
악명 파괴하고, printf
강력한 내장입니다.
지금 나는 사용하고 있습니다 :
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 주위의 래퍼 스크립트입니다 )
ARGS="--ignore $(echo "${TO_IGNORE[@]}" | tr ' ' ',')"
. 연산자 $()
는 백틱보다 강력합니다 ( $()
및의 중첩 허용 ""
). ${TO_IGNORE[@]}
큰 따옴표로 감싸는 것도 도움이됩니다.
여러 문자 구분 기호에 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}"; }
개념에 의한이 아름다움은
추가 예 :
$ 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')을 수행해야합니다. 그러면 위의 명령이 유용합니다.
printf -v bar ...
서브 쉘에서 printf 루프를 실행하고 출력을 캡처하는 대신 사용할 수 있습니다 .
다음은 대부분의 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[@]}"
}
local
)이 전혀 없습니다.
변수 간접 참조를 사용하여 배열을 직접 참조해도 작동합니다. 명명 된 참조도 사용할 수 있지만 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 내 의견 을 참조하십시오 .
__
변수 이름 으로 사용해야하는 특별한 이유가 있습니까? 코드를 실제로 읽을 수 없게 만듭니다.
아마도 나는 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}
이것은 끝에 추가 쉼표를 처리합니다. 나는 배쉬 전문가가 아니다. 이 2c는 더 기초적이고 이해하기 쉽기 때문에