この文字列を変数に格納します。
IN="[email protected];[email protected]"
では、文字列を;
デリミタで分割して、次のようにします。
ADDR1="[email protected]"
ADDR2="[email protected]"
私は必ずしもADDR1
およびADDR2
変数を必要としません。それらが配列の要素であれば、それはさらに優れています。
以下の答えからの提案の後で、私は私が後にいたものである以下に終わりました:
#!/usr/bin/env bash
IN="[email protected];[email protected]"
mails=$(echo $IN | tr ";" "\n")
for addr in $mails
do
echo "> [$addr]"
done
出力:
> [[email protected]]
> [[email protected]]
Internal_field_separator (IFS)を;
に設定することを含む解決策がありました。その答えがどうなったのかよくわかりません。どうやってIFS
をデフォルトに戻しますか?
RE:IFS
解決策、私はこれを試してみましたそしてそれはうまくいきます、私は古いIFS
を保存してそれからそれを復元します
IN="[email protected];[email protected]"
OIFS=$IFS
IFS=';'
mails2=$IN
for x in $mails2
do
echo "> [$x]"
done
IFS=$OIFS
ところで、私が試したとき
mails2=($IN)
$IN
を囲む括弧なしで、ループでそれを印刷するとき、私は最初の文字列だけを手に入れました。
内部フィールド区切り文字 (IFS)変数を設定し、それを解析して配列にすることができます。これがコマンド内で発生すると、IFS
への代入はその単一コマンドの環境(read
へ)にのみ行われます。それはそれからIFS
変数値に従って入力を配列にパースします、そしてそれを繰り返すことができます。
IFS=';' read -ra ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
# process "$i"
done
;
で区切られた1行の項目を解析し、それを配列にプッシュします。 1行の入力が$IN
で区切られるたびに、;
全体を処理するためのもの:
while IFS=';' read -ra ADDR; do
for i in "${ADDR[@]}"; do
# process "$i"
done
done <<< "$IN"
IN="[email protected];[email protected]"
arrIN=(${IN//;/ })
説明:
この構造は、文字列IN
内のすべての';'
(最初の//
はグローバル置換を意味します)を' '
(単一のスペース)に置き換えてから、スペースで区切られたストリングを配列として解釈します(周囲の括弧も同様です)。
各';'
文字を' '
文字で置き換えるために中括弧の内側で使用される構文は、 パラメータ展開 と呼ばれます。
一般的な問題がいくつかあります。
あなたがすぐにそれらを処理することを気にしないならば、私はこれをするのが好きです:
for i in $(echo $IN | tr ";" "\n")
do
# process
done
このようなループを使って配列を初期化することもできますが、おそらくもっと簡単な方法があります。これが助けになれば幸いです。
このSO質問に対して、これを行うには bash の中ですでにさまざまな方法があります。しかしbashにはspecialという多くの機能があり、いわゆるbashismはうまく機能しますが、他の Shell では機能しません。
特に、配列、連想配列、およびパターン置換は純粋なバッシュであり、他のシェルの下では動作しない可能性があります。 _。
私のDebian GNU/Linuxには、 dash と呼ばれる標準シェルがありますが、私は ksh を使うことを好む多くの人々を知っています。
最後に、ごく小さな状況では、彼自身のシェルインタプリタ( ash )を持つ busybox と呼ばれる特別なツールがあります。
SO questionの文字列サンプルは次のとおりです。
IN="[email protected];[email protected]"
これは空白文字と一緒に使用すると便利であり、空白文字はルーチンの結果を変更する可能性があるので、このサンプル文字列を使用することをお勧めします。
IN="[email protected];[email protected];Full Name <[email protected]>"
pure bashでは、配列とIFSを使うことができます。
var="[email protected];[email protected];Full Name <[email protected]>"
oIFS="$IFS" IFS=";" declare -a fields=($var) IFS="$oIFS" unset oIFS
IFS=\; read -a fields <<<"$IN"
最近のbashでこの構文を使用しても、現在のセッションの$IFS
は変更されず、現在のコマンドに対してのみ変更されます。
set | grep ^IFS=
IFS=$' \t\n'
文字列var
は分割され、配列(fields
)に格納されます。
set | grep ^fields=\\\|^var=
fields=([0]="[email protected]" [1]="[email protected]" [2]="Full Name <[email protected]>")
var='[email protected];[email protected];Full Name <[email protected]>'
declare -p
で可変コンテンツをリクエストすることができます:
declare -p IN fields
declare -- IN="[email protected];[email protected];Full Name <[email protected]>"
declare -a fields=([0]="[email protected]" [1]="[email protected]" [2]="Full Name <[email protected]>")
フォークや外部リソースが呼び出されないため、read
は分割を行うためのquickiest方法です。
そこから、あなたは各フィールドを処理するためにあなたがすでに知っているシンタックスを使うことができます:
for x in "${fields[@]}";do
echo "> [$x]"
done
> [[email protected]]
> [[email protected]]
> [Full Name <[email protected]>]
または処理後に各フィールドを削除します(私はこのShiftのアプローチが好きです):
while [ "$fields" ] ;do
echo "> [$fields]"
fields=("${fields[@]:1}")
done
> [[email protected]]
> [[email protected]]
> [Full Name <[email protected]>]
あるいは単純な印刷(短い構文)の場合でも:
printf "> [%s]\n" "${fields[@]}"
> [[email protected]]
> [[email protected]]
> [Full Name <[email protected]>]
あなたはmapfile
で遊ぶことができます:
mapfile -td \; fields < <(printf "%s\0" "$IN")
この構文は特殊文字、改行、空のフィールドを保存します。
空のフィールドを気にしないのであれば、
mapfile -td \; fields <<<"$IN"
fields=("${fields[@]%$'\n'}") # drop '\n' added by '<<<'
しかし、あなたは関数を通してフィールドを使うことができます:
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 '[email protected]', done.
Seq: 1: Sending mail to '[email protected]', done.
Seq: 2: Sending mail to 'Full Name <[email protected]>', done.
または <<<
bash構文で追加された改行を関数にドロップします。
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
同じ出力をレンダリングします。
Seq: 0: Sending mail to '[email protected]', done.
Seq: 1: Sending mail to '[email protected]', done.
Seq: 2: Sending mail to 'Full Name <[email protected]>', done.
しかし、たくさんのシェルで使えるものを書くのであれば、notuse bashismsとする必要があります。
サブストリングのfirstまたはlastの出現箇所にまたがってストリングを分割するための、多くのシェルで使用される構文があります。
${var#*SubStr} # will drop begin of string up to first occur of `SubStr`
${var##*SubStr} # will drop begin of string up to last occur of `SubStr`
${var%SubStr*} # will drop part of string from last occur of `SubStr` to the end
${var%%SubStr*} # will drop part of string from first occur of `SubStr` to the end
(これが欠けているのが私の回答出版の主な理由です。)
Score_Under で指摘されているように、
#
と%
は、一致する可能性のある最短の文字列を削除します。
##
と%%
は、最も長いものを削除します。
#
と##
は、文字列の左から(開始)を意味します。
%
と%%
meand 文字列の右から(末尾)。
この小さなサンプルスクリプトは bash 、 dash 、 ksh 、 busybox でうまく機能し、Mac-OSのbashでもテストされています。
var="[email protected];[email protected];Full Name <[email protected]>"
while [ "$var" ] ;do
iter=${var%%;*}
echo "> [$iter]"
[ "$var" = "$iter" ] && \
var='' || \
var="${var#*;}"
done
> [[email protected]]
> [[email protected]]
> [Full Name <[email protected]>]
楽しむ!
cut
コマンドを参照するいくつかの答えを見ましたが、それらはすべて削除されました。誰もそのことについて詳しく説明していないのはちょっと奇妙です。私はそれがこの種のことをするための、特に区切られたログファイルを解析するためのより有用なコマンドの1つだと思います。
この特定の例をbashスクリプト配列に分割する場合、tr
がおそらくより効率的ですが、cut
を使用することができ、特定のフィールドを中央から引き出す場合はより効果的です。
例:
$ echo "[email protected];[email protected]" | cut -d ";" -f 1
[email protected]
$ echo "[email protected];[email protected]" | cut -d ";" -f 2
[email protected]
明らかにそれをループに入れ、-fパラメータを繰り返して各フィールドを個別に引っ張ることができます。
このような行を持つ区切りログファイルがある場合、これはより便利になります。
2015-04-27|12345|some action|an attribute|meta data
cut
はこのファイルをcat
することができ、さらなる処理のために特定のフィールドを選択するのに非常に便利です。
これは私のために働いた:
string="1;2"
echo $string | cut -d';' -f1 # output is 1
echo $string | cut -d';' -f2 # output is 2
このアプローチはどうでしょうか。
IN="[email protected];[email protected]"
set -- "$IN"
IFS=";"; declare -a Array=($*)
echo "${Array[@]}"
echo "${Array[0]}"
echo "${Array[1]}"
echo "[email protected];[email protected]" | sed -e 's/;/\n/g'
[email protected]
[email protected]
これも動作します:
IN="[email protected];[email protected]"
echo ADD1=`echo $IN | cut -d \; -f 1`
echo ADD2=`echo $IN | cut -d \; -f 2`
注意してください、この解決策は常に正しいとは限りません。あなたが "[email protected]"だけを渡す場合、それはADD1とADD2の両方にそれを割り当てます。
_ awk _ はあなたの問題を解決するための最良かつ効率的なコマンドだと思います。 AWKはほとんどすべてのLinuxディストリビューションのデフォルトでBashに含まれています。
echo "[email protected];[email protected]" | awk -F';' '{print $1,$2}'
あげる
[email protected] [email protected]
もちろん、あなたはawkのprintフィールドを再定義することによって各Eメールアドレスを保存することができます。
Darronの答え 、これは私のやり方です。
IN="[email protected];[email protected]"
read ADDR1 ADDR2 <<<$(IFS=";"; echo $IN)
強固な方法であるBashでは、変数に改行が含まれていても機能します。
IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")
見て:
$ in=$'one;two three;*;there is\na newline\nin this field'
$ IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")
$ declare -p array
declare -a array='([0]="one" [1]="two three" [2]="*" [3]="there is
a newline
in this field")'
これを機能させるためのトリックは、空の区切り文字を持つread
(delimiter)の-d
オプションを使用することです。そのため、read
は、入力されたものすべてを強制的に読み取るようになります。そしてread
のおかげで、最後の改行なしで、in
に変数printf
の内容を正確に入れます。 printf
に渡される文字列が末尾の区切り文字を持つようにするために、read
にも区切り文字を入れていることに注意してください。それがなければ、read
は末尾の空のフィールドを削除する可能性があります。
$ in='one;two;three;' # there's an empty field
$ IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")
$ declare -p array
declare -a array='([0]="one" [1]="two" [2]="three" [3]="")'
末尾の空のフィールドは保持されます。
Bash 4.4以降、組み込みのmapfile
(別名readarray
)は、区切り文字を指定するための-d
オプションをサポートします。それゆえ、もう一つの標準的な方法は、
mapfile -d ';' -t array < <(printf '%s;' "$in")
配列を使用していない場合は、この1つのライナーはどうでしょうか。
IFS=';' read ADDR1 ADDR2 <<<$IN
これはきれいな3ライナーです:
in="foo@bar;bizz@buzz;fizz@buzz;buzz@woof"
IFS=';' list=($in)
for item in "${list[@]}"; do echo $item; done
ここでIFS
は区切り文字に基づいて単語を区切り、()
は array を作成するために使用されます。その後、[@]
を使用して各項目を個別のWordとして返します。
それ以降にコードがある場合は、$IFS
も復元する必要があります。 unset IFS
。
IFSを設定せずに
コロンが1つしかない場合は、それを実行できます。
a="foo:bar"
b=${a%:*}
c=${a##*:}
あなたが得るでしょう:
b = foo
c = bar
次のBash/zsh関数は、最初の引数を2番目の引数で指定された区切り文字に分割します。
split() {
local string="$1"
local delimiter="$2"
if [ -n "$string" ]; then
local part
while read -d "$delimiter" part; do
echo $part
done <<< "$string"
echo $part
fi
}
例えば、
$ split 'a;b;c' ';'
収量
a
b
c
この出力は、例えば他のコマンドにパイプで送ることができます。例:
$ split 'a;b;c' ';' | cat -n
1 a
2 b
3 c
与えられた他の解決策と比較して、これには以下の利点があります。
IFS
name__はオーバーライドされません。ローカル変数でも動的スコープのため、ループ上でIFS
name__をオーバーライドすると、ループ内から実行される関数呼び出しに新しい値がリークします。
配列は使用されません:read
name__を使用して文字列を配列に読み込むには、Bashでは-a
、zshでは-A
のフラグが必要です。
必要に応じて、以下のように関数をスクリプトに入れることができます。
#!/usr/bin/env bash
split() {
# ...
}
split "$@"
このようなシンプルでスマートな方法があります。
echo "add:sfff" | xargs -d: -i echo {}
しかし、あなたはgnu xargsを使わなければなりません、BSD xargsは-d delimをサポートできません。あなたが私のようにアップルのMacを使っているなら。 gnu xargsをインストールすることができます。
brew install findutils
それから
echo "add:sfff" | gxargs -d: -i echo {}
あなたは多くの状況にawkを適用することができます
echo "[email protected];[email protected]"|awk -F';' '{printf "%s\n%s\n", $1, $2}'
これも使えます
echo "[email protected];[email protected]"|awk -F';' '{print $1,$2}' OFS="\n"
これが最も簡単な方法です。
spo='one;two;three'
OIFS=$IFS
IFS=';'
spo_array=($spo)
IFS=$OIFS
echo ${spo_array[*]}
ここではいくつかの素晴らしい答えがありますが(他の言語で分割するのと似たようなもの - これは私が最初の質問で言ったことです) - 私はこれを解決しました。
IN="[email protected];[email protected]"
declare -a a="(${IN/;/ })";
今${a[0]}
、${a[1]}
などはあなたが期待する通りです。用語の数には${#a[*]}
を使用してください。あるいは繰り返しますが、もちろん:
for i in ${a[*]}; do echo $i; done
重要な注意点:
これは私が問題を解決したが、あなたの問題を解決しないかもしれない、心配するスペースがない場合にはうまくいきます。その場合は$IFS
ソリューションを使用してください。
IN="[email protected];[email protected]"
IFS=';'
read -a IN_arr <<< "${IN}"
for entry in "${IN_arr[@]}"
do
echo $entry
done
出力
[email protected]
[email protected]
システム:Ubuntu 12.04.1
どちらもbash配列を必要としない2つのボーンっぽい方法:
ケース1 :簡潔に保つ:レコード区切り文字としてNewLineを使用してください。
IN="[email protected]
[email protected]"
while read i; do
# process "$i" ... eg.
echo "[email:$i]"
done <<< "$IN"
注:この最初のケースでは、リスト操作を補助するためにサブプロセスはフォークされません。
考え:多分それはNLを広範囲に internal を使って、そして最終結果を生成するときだけ別のRSに変換する価値があります external .
ケース2 : ";"を使用レコードセパレータとして...例えば。
NL="
" IRS=";" ORS=";"
conv_IRS() {
exec tr "$1" "$NL"
}
conv_ORS() {
exec tr "$NL" "$1"
}
IN="[email protected];[email protected]"
IN="$(conv_IRS ";" <<< "$IN")"
while read i; do
# process "$i" ... eg.
echo -n "[email:$i]$ORS"
done <<< "$IN"
どちらの場合も、サブリストはループ内で構成でき、ループが完了した後も持続します。これは、リストをファイルに保存する代わりにメモリ内のリストを操作するときに便利です。 {p.s.落ち着いて、B-)を続けてください。
スペースがないのなら、どうしてこれじゃないの?
IN="[email protected];[email protected]"
arr=(`echo $IN | tr ';' ' '`)
echo ${arr[0]}
echo ${arr[1]}
すでに提供されている素晴らしい答えは別として、それが単にデータを印刷することの問題であるならば、あなたはawk
を使うことを考慮するかもしれません:
awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "$IN"
これはフィールドセパレータを;
に設定するので、それはfor
ループでフィールドをループし、それに応じて印刷することができます。
$ IN="[email protected];[email protected]"
$ awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "$IN"
> [[email protected]]
> [[email protected]]
別の入力で:
$ awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "a;b;c d;e_;f"
> [a]
> [b]
> [c d]
> [e_]
> [f]
大丈夫みんな!
これが私の答えです!
DELIMITER_VAL='='
read -d '' F_ABOUT_DISTRO_R <<"EOF"
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.4 LTS"
NAME="Ubuntu"
VERSION="14.04.4 LTS, Trusty Tahr"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 14.04.4 LTS"
VERSION_ID="14.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
EOF
SPLIT_NOW=$(awk -F$DELIMITER_VAL '{for(i=1;i<=NF;i++){printf "%s\n", $i}}' <<<"${F_ABOUT_DISTRO_R}")
while read -r line; do
SPLIT+=("$line")
done <<< "$SPLIT_NOW"
for i in "${SPLIT[@]}"; do
echo "$i"
done
なぜこのアプローチが私にとって「最善」なのでしょうか。
理由は2つあります。
[]
IN='[email protected];[email protected];Charlie Brown <[email protected];!"#$%&/()[]{}*? are no problem;simple is beautiful :-)'
set -f
oldifs="$IFS"
IFS=';'; arrayIN=($IN)
IFS="$oldifs"
for i in "${arrayIN[@]}"; do
echo "$i"
done
set +f
出力:
[email protected]
[email protected]
Charlie Brown <[email protected]
!"#$%&/()[]{}*? are no problem
simple is beautiful :-)
説明:括弧()を使用した単純代入は、セミコロンで区切られたリストを配列に変換します。標準のFORループは、通常通りその配列内の個々の項目を処理します。 IN変数に与えられたリストは "ハード"クォートでなければならない、つまりシングルティックでなければならないことに注意してください。
Bashは代入をコマンドと同じようには扱わないため、IFSを保存して復元する必要があります。別の回避策は、関数内で割り当てをラップし、修正されたIFSを使用してその関数を呼び出すことです。その場合、IFSを個別に保存/復元する必要はありません。それを指摘してくれて "Bize"をありがとう。
Androidシェルでは、提案されている方法のほとんどは機能しません。
$ IFS=':' read -ra ADDR <<<"$PATH"
/system/bin/sh: can't create temporary file /sqlite_stmt_journals/mksh.EbNoR10629: No such file or directory
仕事は何ですか:
$ for i in ${PATH//:/ }; do echo $i; done
/sbin
/vendor/bin
/system/sbin
/system/bin
/system/xbin
//
はグローバル置換を意味します。
set
組み込みを使用して$@
配列をロードします。
IN="[email protected];[email protected]"
IFS=';'; set $IN; IFS=$' \t\n'
その後、パーティーを始めましょう:
echo $#
for a; do echo $a; done
ADDR1=$1 ADDR2=$2
最も洗練された解決策ではないかもしれませんが、*
とスペースで動作します。
IN="bla@so me.com;*;[email protected]"
for i in `delims=${IN//[^;]}; seq 1 $((${#delims} + 1))`
do
echo "> [`echo $IN | cut -d';' -f$i`]"
done
アウトプット
> [bla@so me.com]
> [*]
> [[email protected]]
その他の例(最初と最後の区切り文字):
IN=";bla@so me.com;*;[email protected];"
> []
> [bla@so me.com]
> [*]
> [[email protected]]
> []
基本的には;
以外のすべての文字を削除してdelims
を作ります。 ;;;
。それから1
でカウントされるようにnumber-of-delimiters
から${#delims}
へのfor
ループを行います。最後のステップはcut
を使って$i
番目の部分を安全に取得することです。
';'で区切られた文字列を分割するためのワンライナー配列にする:
IN="[email protected];[email protected]"
ADDRS=( $(IFS=";" echo "$IN") )
echo ${ADDRS[0]}
echo ${ADDRS[1]}
これはIFSをサブシェルに設定するだけなので、その値を保存したり復元したりする必要はありません。
これは空白文字も扱います。
IFS=';' read ADDR1 ADDR2 <<< $(echo ${IN})