Bashにこのような配列がある場合:
FOO=( a b c )
要素をコンマで結合するにはどうすればよいですか?たとえば、を生成しa,b,c
ます。
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
konsolebox
スタイルを使用してください:) function join { local IFS=$1; __="${*:2}"; }
またはfunction join { IFS=$1 eval '__="${*:2}"'; }
。その後、使用__
してください。はい、__
結果変数としての使用を促進しているのは私です;)(および一般的な反復変数または一時変数)。コンセプトが人気のBash wikiサイトに
$d
の形式指定子に拡張を入れないでくださいprintf
。あなたは「エスケープ」したので安全だと思います%
が、他にも注意点があります。区切り文字にバックスラッシュが含まれている場合(例:)、\n
または区切り文字がハイフンで始まっている場合(そして、今では考えられない他のもの)。もちろん、これらを修正することもできます(バックスラッシュを2つのバックスラッシュに置き換えて、を使用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[@]}"
。それは1つfork
少なくなります(実際にはclone
)。ファイルの読み取りをフォークすることもできます:printf -v bar ",%s" $(<infile)
。
$separator
が含まれていない%s
かなど、あなたが作ることができるprintf
堅牢な:printf "%s%s" "$separator" "${foo[@]}"
。
printf "%s%s"
単純に、残りの引数連結のみ、出力の設定された第1のインスタンスでのセパレータを使用します。
printf "%s" "${foo[@]/#/$separator}"
。
IFS=; regex="${foo[*]/#/$separator}"
。この時点で、これは本質的にgniourf_gniourfの答えになり、IMOは最初からよりきれいです。つまり、関数を使用してIFS変更と一時変数の範囲を制限します。
$ foo=(a "b c" d)
$ bar=$(IFS=, ; echo "${foo[*]}")
$ echo "$bar"
a,b c,d
bar=$( IFS=, ; echo "${foo[*]}" )
@
代わりにを使用すると*
、これが機能しない理由はあり$(IFS=, ; echo "${foo[@]}")
ますか?*
が要素内の空白を既に保持していることがわかり@
ます。これは通常、このために必要なためです。
*
です。bashのmanページでは、次の「特殊パラメータ」と説明のための外観を検索*
:
たぶん、例えば
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
してすべてを1行に結合します。
tr " " "\n" <<< "$FOO" | paste -sd , -
結果:
a,b,c
これは私にとって最も速くてきれいなようです!
$FOO
ただし、配列の最初の要素にすぎません。また、これはスペースを含む配列要素では機能しません。
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ソリューション(@は問題ではありません)
#/!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 ' ' ','`"
これは機能しますが、(一般的な場合)配列要素にスペースがあると、ひどく壊れてしまいます。
ARGS="--ignore $(echo "${TO_IGNORE[@]}" | tr ' ' ',')"
。演算子$()
はbackticsよりも強力です($()
andのネストを許可します""
)。${TO_IGNORE[@]}
二重引用符で囲むことも役立つはずです。
複数文字のセパレーターにはperlを使用します。
function join {
perl -e '$s = shift @ARGV; print join($s, @ARGV);' "$@";
}
join ', ' a b c # a, b, c
または1行で:
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[@]}"
}
変数の間接参照を使用して配列を直接参照することもできます。名前付き参照も使用できますが、4.3でのみ使用可能になりました。
この形式の関数を使用する利点は、区切り文字をオプションにすることができることです(デフォルトはデフォルトの最初の文字になります)。 IFS
あるスペースです。て空の文字列にすることもできます)、値を2度展開することを回避できます(最初にパラメータとして渡されたとき、および"$@"
関数内として渡されたとき)。
このソリューションでは、ユーザーがコマンド置換内で関数を呼び出す必要もありません。サブシェルを呼び出して、別の変数に割り当てられた文字列の結合バージョンを取得します。
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-alphaまで機能します。観察されたように、変数の間接化は変数だけでなく他のパラメーターでも機能します。
パラメータは、値を格納するエンティティです。名前、番号、または以下の「特殊パラメーター」にリストされている特殊文字のいずれかになります。変数は、名前で示されるパラメーターです。
配列と配列要素もパラメータ(値を格納するエンティティ)であり、配列への参照は技術的にはパラメータへの参照でもあります。そして特別なパラメータ@
と同様に、array[@]
同様に、有効な参照も作成します。
パラメーター自体からの参照を逸脱する変更または選択された形式の拡張(サブストリング拡張など)は機能しなくなりました。
Bash 5.0のリリースバージョンでは、変数の間接参照はすでに間接展開と呼ばれ、その動作はマニュアルに明示的に文書化されています。
パラメーターの最初の文字が感嘆符(!)であり、パラメーターが名前参照でない場合は、間接参照のレベルが導入されます。Bashは、残りのパラメーターを展開することによって形成された値を新しいパラメーターとして使用します。次に、これが拡張され、その値が、元のパラメーターの拡張ではなく、残りの拡張で使用されます。これは間接展開と呼ばれます。
のドキュメントでは${parameter}
、parameter
は「(パラメータ)または配列参照で説明されているシェルパラメータ」と呼ばれていることに注意してください。また、配列のドキュメントでは、「配列の任意の要素は、を使用して参照される可能性が${name[subscript]}
ある」と記載されています。 これは__r[@]
、配列参照が作成されます。
Riccardo Galliの回答で私のコメントを参照してください。
__
変数名として使用する特定の理由はありますか?コードを本当に読めなくします。
たぶん私は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}
これにより、最後の余分なコンマも処理されます。私はbashのエキスパートではありません。これはより初歩的で理解しやすいので、ちょうど私の2c