互換性のある答え
これを行うには多くの異なる方法があります バッシュ。
ただし、bash
多くの特別な機能(いわゆるバシズム)があることに最初に注意することが重要ですには他の機能では動作しない)があることシェル。
特に、この投稿のソリューションやスレッド内の他のソリューションで使用されている配列、連想配列、パターン置換はバシズムであり、多くの人が使用する他のシェルでは機能しない可能性があります。
たとえば、私のDebian GNU / Linuxには、標準があります。シェルと呼ばれるものがあります。ダッシュ; と呼ばれる別のシェルを使用したい多くの人々を知っていますksh; と呼ばれる特別なツールもありますbusybox 彼自身のシェルインタープリター(灰)。
リクエストされた文字列
上記の質問で分割される文字列は次のとおりです。
IN="bla@some.com;john@home.com"
この文字列の変更バージョンを使用して、私のソリューションが空白を含む文字列に対して堅牢であることを確認します。
IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
区切り文字に基づいて文字列を分割 バッシュ (バージョン> = 4.2)
純粋な bash
、我々が作成することができ、アレイのための一時的な値で要素分割とをIFS(入力フィールドセパレータ)。とりわけ、IFSはbash
、配列を定義するときに要素間の区切り文字としてどの文字を処理するかを指示します。
IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
# save original IFS value so we can restore it later
oIFS="$IFS"
IFS=";"
declare -a fields=($IN)
IFS="$oIFS"
unset oIFS
の新しいバージョンでbash
は、コマンドの前にIFS定義を付けると、そのコマンドのIFS のみが変更され、その後すぐに以前の値にリセットされます。これは、上記を1行で実行できることを意味します。
IFS=\; read -a fields <<<"$IN"
# after this command, the IFS resets back to its previous value (here, the default):
set | grep ^IFS=
# IFS=$' \t\n'
文字列IN
がfields
セミコロンで分割されたという名前の配列に格納されていることがわかります。
set | grep ^fields=\\\|^IN=
# fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name <fulnam@other.org>")
# IN='bla@some.com;john@home.com;Full Name <fulnam@other.org>'
(これらの変数の内容をdeclare -p
:を使用して表示することもできます。)
declare -p IN fields
# declare -- IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
# declare -a fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name <fulnam@other.org>")
注read
ある最速全く存在しないため、分割を行う方法フォークと呼ばれる、または外部のリソースが。
配列を定義したら、単純なループを使用して、各フィールド(または、定義した配列内の各要素)を処理できます。
# `"${fields[@]}"` expands to return every element of `fields` array as a separate argument
for x in "${fields[@]}" ;do
echo "> [$x]"
done
# > [bla@some.com]
# > [john@home.com]
# > [Full Name <fulnam@other.org>]
または、シフトアプローチを使用して処理した後、配列から各フィールドを削除することもできます。
while [ "$fields" ] ;do
echo "> [$fields]"
# slice the array
fields=("${fields[@]:1}")
done
# > [bla@some.com]
# > [john@home.com]
# > [Full Name <fulnam@other.org>]
配列の単純な出力だけが必要な場合は、ループする必要さえありません。
printf "> [%s]\n" "${fields[@]}"
# > [bla@some.com]
# > [john@home.com]
# > [Full Name <fulnam@other.org>]
更新:最近 バッシュ > = 4.4
の新しいバージョンではbash
、次のコマンドで遊ぶこともできますmapfile
。
mapfile -td \; fields < <(printf "%s\0" "$IN")
この構文は、特殊文字、改行、空のフィールドを保持します!
空のフィールドを含めたくない場合は、次のようにします。
mapfile -td \; fields <<<"$IN"
fields=("${fields[@]%$'\n'}") # drop '\n' added by '<<<'
を使用mapfile
すると、配列の宣言をスキップして、区切られた要素を暗黙的に「ループ」し、それぞれに対して関数を呼び出すこともできます。
myPubliMail() {
printf "Seq: %6d: Sending mail to '%s'..." $1 "$2"
# mail -s "This is not a spam..." "$2" </path/to/body
printf "\e[3D, done.\n"
}
mapfile < <(printf "%s\0" "$IN") -td \; -c 1 -C myPubliMail
(注:\0
フォーマット文字列の最後にある空のフィールドが気にならないか、存在しない場合は、フォーマット文字列の最後は役に立ちません。)
mapfile < <(echo -n "$IN") -td \; -c 1 -C myPubliMail
# Seq: 0: Sending mail to 'bla@some.com', done.
# Seq: 1: Sending mail to 'john@home.com', done.
# Seq: 2: Sending mail to 'Full Name <fulnam@other.org>', done.
または、を使用<<<
して、関数本体に、追加した改行を削除するための処理を含めることができます。
myPubliMail() {
local seq=$1 dest="${2%$'\n'}"
printf "Seq: %6d: Sending mail to '%s'..." $seq "$dest"
# mail -s "This is not a spam..." "$dest" </path/to/body
printf "\e[3D, done.\n"
}
mapfile <<<"$IN" -td \; -c 1 -C myPubliMail
# Renders the same output:
# Seq: 0: Sending mail to 'bla@some.com', done.
# Seq: 1: Sending mail to 'john@home.com', done.
# Seq: 2: Sending mail to 'Full Name <fulnam@other.org>', done.
区切り文字に基づいて文字列を分割 シェル
を使用できない場合bash
、または多くの異なるシェルで使用できるものを作成する場合は、bashismを使用できないことがよくあります。これには、上記のソリューションで使用していた配列が含まれます。
ただし、文字列の「要素」をループするために配列を使用する必要はありません。多くのシェルでは、パターンの最初または最後の出現から文字列の部分文字列を削除するために使用される構文があります。注*
ゼロ以上の文字を表しますワイルドカードです。
(これまでに投稿されたソリューションでこのアプローチが欠如していることが、この回答を書いている主な理由です。)
${var#*SubStr} # drops substring from start of string up to first occurrence of `SubStr`
${var##*SubStr} # drops substring from start of string up to last occurrence of `SubStr`
${var%SubStr*} # drops substring from last occurrence of `SubStr` to end of string
${var%%SubStr*} # drops substring from first occurrence of `SubStr` to end of string
Score_Underで説明したように:
#
そして%
、文字列の最初と最後から、可能な限り最短の一致する部分文字列を削除します。
##
そして%%
、最長の一致する部分文字列を削除します。
上記の構文を使用して、デリミタまでまたはデリミタの後のサブストリングを削除することにより、ストリングからサブストリング「要素」を抽出するアプローチを作成できます。
以下のコードブロックは、 バッシュ (Mac OSを含む bash
)、ダッシュ、 ksh、および busyboxの 灰:
IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
while [ "$IN" ] ;do
# extract the substring from start of string up to delimiter.
# this is the first "element" of the string.
iter=${IN%%;*}
echo "> [$iter]"
# if there's only one element left, set `IN` to an empty string.
# this causes us to exit this `while` loop.
# else, we delete the first "element" of the string from IN, and move onto the next.
[ "$IN" = "$iter" ] && \
IN='' || \
IN="${IN#*;}"
done
# > [bla@some.com]
# > [john@home.com]
# > [Full Name <fulnam@other.org>]
楽しんで!