tra-loi-cau-hoi-phat-trien-web.com

Làm cách nào để tách một chuỗi trên một dấu phân cách trong Bash?

Tôi có chuỗi này được lưu trữ trong một biến:

IN="[email protected];[email protected]"

Bây giờ tôi muốn phân tách các chuỗi bằng dấu phân cách ; để tôi có:

ADDR1="[email protected]"
ADDR2="[email protected]"

Tôi không nhất thiết cần các biến ADDR1ADDR2. Nếu chúng là các phần tử của một mảng thậm chí còn tốt hơn.


Sau những gợi ý từ các câu trả lời dưới đây, tôi đã kết thúc với những điều sau đây là:

#!/usr/bin/env bash

IN="[email protected];[email protected]"

mails=$(echo $IN | tr ";" "\n")

for addr in $mails
do
    echo "> [$addr]"
done

Đầu ra:

> [[email protected]]
> [[email protected]]

Có một giải pháp liên quan đến cài đặt Internal_field_separator (IFS) thành ;. Tôi không chắc điều gì đã xảy ra với câu trả lời đó, làm thế nào để bạn đặt lại IFS trở lại mặc định?

RE: IFS giải pháp, tôi đã thử cách này và nó hoạt động, tôi giữ IFS cũ và sau đó khôi phục nó:

IN="[email protected];[email protected]"

OIFS=$IFS
IFS=';'
mails2=$IN
for x in $mails2
do
    echo "> [$x]"
done

IFS=$OIFS

BTW, khi tôi đã cố gắng

mails2=($IN)

Tôi chỉ nhận được chuỗi đầu tiên khi in nó trong vòng lặp, không có dấu ngoặc quanh $IN nó hoạt động.

1733
stefanB

Bạn có thể đặt biến dấu tách trường nội bộ (IFS), sau đó để nó phân tích thành một mảng. Khi điều này xảy ra trong một lệnh, thì việc gán cho IFS chỉ diễn ra trong môi trường của lệnh đó (tới read). Sau đó, nó phân tích cú pháp đầu vào theo giá trị biến IFS thành một mảng, sau đó chúng ta có thể lặp lại.

IFS=';' read -ra ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
    # process "$i"
done

Nó sẽ phân tích một dòng các mục được phân tách bằng ;, đẩy nó vào một mảng. Công cụ xử lý toàn bộ $IN, mỗi lần một dòng đầu vào được phân tách bằng ;:

 while IFS=';' read -ra ADDR; do
      for i in "${ADDR[@]}"; do
          # process "$i"
      done
 done <<< "$IN"
1067

Lấy từMảng phân tách tập lệnh Bash Shell:

IN="[email protected];[email protected]"
arrIN=(${IN//;/ })

Giải trình:

Cấu trúc này thay thế tất cả các lần xuất hiện của ';' (// ban đầu có nghĩa là thay thế toàn cầu) trong chuỗi IN bằng ' ' (một khoảng trắng), sau đó diễn giải chuỗi được phân tách bằng dấu cách là một mảng (đó là những dấu ngoặc đơn xung quanh làm).

Cú pháp được sử dụng bên trong dấu ngoặc nhọn để thay thế mỗi ký tự ';' bằng ký tự ' ' được gọi là Mở rộng tham số .

Có một số vấn đề phổ biến:

  1. Nếu chuỗi gốc có khoảng trắng, bạn sẽ cần sử dụng IFS : [.__.]
    • IFS=':'; arrIN=($IN); unset IFS;
  2. Nếu chuỗi ban đầu có khoảng trắngdấu phân cách là một dòng mới, bạn có thể đặt IFS với: [.__.]
    • IFS=$'\n'; arrIN=($IN); unset IFS;
843
palindrom

Nếu bạn không ngại xử lý chúng ngay lập tức, tôi thích làm điều này:

for i in $(echo $IN | tr ";" "\n")
do
  # process
done

Bạn có thể sử dụng loại vòng lặp này để khởi tạo một mảng, nhưng có lẽ có một cách dễ dàng hơn để làm điều đó. Hy vọng điều này sẽ giúp, mặc dù.

220
Chris Lutz

Câu trả lời tương thích

Đối với câu hỏi SO này, đã có rất nhiều cách khác nhau để làm điều này trong bash . Nhưng bash có nhiều tính năng đặc biệt, nên được gọi là bashism hoạt động tốt, nhưng nó sẽ không hoạt động trong bất kỳ Shell nào khác.

Cụ thể, mảng, mảng kết hợpthay thế mẫubashism thuần túy và có thể không hoạt động dưới các shell khác.

Trên Debian GNU/Linux của tôi, có một Standard Shell được gọi là dash , nhưng tôi biết nhiều người thích sử dụng ksh .

Cuối cùng, trong một tình huống rất nhỏ, có một công cụ đặc biệt gọi là busybox với trình thông dịch Shell riêng của mình ( tro ).

Chuỗi yêu cầu

Mẫu chuỗi trong câu hỏi SO là:

IN="[email protected];[email protected]"

Vì điều này có thể hữu ích với whitespaces và vì whitespaces có thể sửa đổi kết quả của thói quen, tôi thích sử dụng chuỗi mẫu này:

 IN="[email protected];[email protected];Full Name <[email protected]>"

Chia chuỗi dựa trên dấu phân cách trong bash (phiên bản> = 4.2)

Trong thuần bash, chúng tôi có thể sử dụng mảngIFS:

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"

Sử dụng cú pháp này trong bash gần đây không thay đổi $IFS cho phiên hiện tại, nhưng chỉ cho lệnh hiện tại:

set | grep ^IFS=
IFS=$' \t\n'

Bây giờ, chuỗi var được phân tách và lưu trữ thành một mảng (có tên 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]>'

Chúng tôi có thể yêu cầu nội dung thay đổi với 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 là cách nhanh nhất để thực hiện phân tách, vì không có forks và không có tài nguyên bên ngoài nào được gọi.

Từ đó, bạn có thể sử dụng cú pháp bạn đã biết để xử lý từng trường:

for x in "${fields[@]}";do
    echo "> [$x]"
    done
> [[email protected]]
> [[email protected]]
> [Full Name <[email protected]>]

hoặc bỏ từng trường sau khi xử lý (tôi thích cách tiếp cận shift này):

while [ "$fields" ] ;do
    echo "> [$fields]"
    fields=("${fields[@]:1}")
    done
> [[email protected]]
> [[email protected]]
> [Full Name <[email protected]>]

hoặc thậm chí cho bản in đơn giản (cú pháp ngắn hơn):

printf "> [%s]\n" "${fields[@]}"
> [[email protected]]
> [[email protected]]
> [Full Name <[email protected]>]

Cập nhật: gần đây bash > = 4.4

Bạn có thể chơi với mapfile:

mapfile -td \; fields < <(printf "%s\0" "$IN")

Cú pháp này bảo tồn các ký tự đặc biệt, dòng mới và các trường trống!

Nếu bạn không quan tâm đến các trường trống, bạn có thể:

mapfile -td \; fields <<<"$IN"
fields=("${fields[@]%$'\n'}")   # drop '\n' added by '<<<'

Nhưng bạn có thể sử dụng các trường thông qua chức năng:

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

(Nota: \0 ở cuối chuỗi định dạng là vô ích trong khi bạn không quan tâm đến các trường trống ở cuối chuỗi)

mapfile < <(echo -n "$IN") -td \; -c 1 -C myPubliMail

Sẽ hiển thị một cái gì đó như:

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.

Hoặc là Thả dòng mới được thêm bởi cú pháp bash <<< trong hàm:

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

Sẽ hiển thị cùng một đầu ra:

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.

Chia chuỗi dựa trên dấu phân cách trong Shell

Nhưng nếu bạn sẽ viết một cái gì đó có thể sử dụng được dưới nhiều vỏ, bạn phải không sử dụng bashism.

Có một cú pháp, được sử dụng trong nhiều shell, để phân tách một chuỗi trên first hoặc last xuất hiện của một chuỗi con:

${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

(Sự thiếu sót này là lý do chính của ấn phẩm câu trả lời của tôi;)

Như được chỉ ra bởi Score_Under :

#% xóa chuỗi kết hợp ngắn nhất có thể và

##%% xóa lâu nhất có thể.

trong đó ### có nghĩa là từ trái (bắt đầu) của chuỗi và

%%% meand từ phải (cuối) của chuỗi.

Tập lệnh mẫu nhỏ này hoạt động tốt trong bash , dash , ksh , busybox và cũng đã được thử nghiệm trong bash của Mac-OS

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]>]

Chúc vui vẻ!

159
F. Hauri

Tôi đã thấy một vài câu trả lời tham khảo lệnh cut, nhưng tất cả chúng đều đã bị xóa. Có một điều kỳ lạ là không ai giải thích được điều đó, vì tôi nghĩ đó là một trong những lệnh hữu ích hơn để thực hiện loại điều này, đặc biệt là để phân tích các tệp nhật ký được phân tách.

Trong trường hợp tách ví dụ cụ thể này thành một mảng tập lệnh bash, tr có thể hiệu quả hơn, nhưng có thể sử dụng cut và hiệu quả hơn nếu bạn muốn kéo các trường cụ thể từ giữa.

Thí dụ:

$ echo "[email protected];[email protected]" | cut -d ";" -f 1
[email protected]
$ echo "[email protected];[email protected]" | cut -d ";" -f 2
[email protected]

Rõ ràng bạn có thể đặt nó vào một vòng lặp và lặp lại tham số -f để kéo từng trường một cách độc lập.

Điều này hữu ích hơn khi bạn có tệp nhật ký được phân tách bằng các hàng như thế này:

2015-04-27|12345|some action|an attribute|meta data

cut rất tiện dụng để có thể cat tệp này và chọn một trường cụ thể để xử lý thêm.

124
DougW

Điều này làm việc cho tôi:

string="1;2"
echo $string | cut -d';' -f1 # output is 1
echo $string | cut -d';' -f2 # output is 2
91
Steven Lizarazo

Cách tiếp cận này:

IN="[email protected];[email protected]" 
set -- "$IN" 
IFS=";"; declare -a Array=($*) 
echo "${Array[@]}" 
echo "${Array[0]}" 
echo "${Array[1]}" 

Nguồn

83
errator
62
lothar

Điều này cũng hoạt động:

IN="[email protected];[email protected]"
echo ADD1=`echo $IN | cut -d \; -f 1`
echo ADD2=`echo $IN | cut -d \; -f 2`

Hãy cẩn thận, giải pháp này không phải lúc nào cũng đúng. Trong trường hợp bạn chỉ vượt qua "[email protected]", nó sẽ gán nó cho cả ADD1 và ADD2.

61
Ashok

Tôi nghĩ AWK là lệnh tốt nhất và hiệu quả để giải quyết vấn đề của bạn. AWK được bao gồm trong Bash theo mặc định trong hầu hết mọi bản phân phối Linux.

echo "[email protected];[email protected]" | awk -F';' '{print $1,$2}'

sẽ cho

[email protected] [email protected]

Tất nhiên, bạn có thể lưu trữ từng địa chỉ email bằng cách xác định lại trường in awk.

38
Tony

Một cách khác về Câu trả lời của Darron , đây là cách tôi thực hiện:

IN="[email protected];[email protected]"
read ADDR1 ADDR2 <<<$(IFS=";"; echo $IN)
30
nickjb

Trong Bash, một cách chống đạn, sẽ hoạt động ngay cả khi biến của bạn chứa dòng mới:

IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")

Nhìn:

$ 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")'

Mẹo để làm việc này là sử dụng tùy chọn -d của read (dấu phân cách) với một dấu phân cách trống, để read buộc phải đọc mọi thứ được cung cấp. Và chúng tôi cung cấp read với chính xác nội dung của biến in, không có dòng mới nào nhờ printf. Lưu ý rằng chúng tôi cũng đặt dấu phân cách trong printf để đảm bảo rằng chuỗi được truyền cho read có dấu phân cách. Không có nó, read sẽ cắt các trường trống tiềm ẩn:

$ 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]="")'

các lĩnh vực trống trailing được bảo tồn.


Cập nhật cho Bash≥4.4

Kể từ Bash 4.4, hàm dựng sẵn mapfile (còn gọi là readarray) hỗ trợ tùy chọn -d để chỉ định một dấu phân cách. Do đó một cách kinh điển khác là:

mapfile -d ';' -t array < <(printf '%s;' "$in")
26
gniourf_gniourf

Làm thế nào về một lớp lót này, nếu bạn không sử dụng mảng:

IFS=';' read ADDR1 ADDR2 <<<$IN
20
Darron

Dưới đây là 3 lớp lót sạch sẽ:

in="[email protected];[email protected];[email protected];[email protected]"
IFS=';' list=($in)
for item in "${list[@]}"; do echo $item; done

trong đó IFS phân định các từ dựa trên dấu phân cách và () được sử dụng để tạo một mảng . Sau đó, [@] được sử dụng để trả về mỗi mục dưới dạng một từ riêng biệt.

Nếu sau đó bạn có bất kỳ mã nào, bạn cũng cần khôi phục $IFS, ví dụ: unset IFS.

19
kenorb

Không cần thiết lập IFS

Nếu bạn chỉ có một dấu hai chấm, bạn có thể làm điều đó:

a="foo:bar"
b=${a%:*}
c=${a##*:}

bạn sẽ nhận được:

b = foo
c = bar
16
Emilien Brigand

Hàm Bash/zsh sau đây phân tách đối số đầu tiên của nó trên dấu phân cách được đưa ra bởi đối số thứ hai:

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
}

Ví dụ, lệnh

$ split 'a;b;c' ';'

sản lượng

a
b
c

Đầu ra này có thể, ví dụ, được dẫn đến các lệnh khác. Thí dụ:

$ split 'a;b;c' ';' | cat -n
1   a
2   b
3   c

So với các giải pháp khác được đưa ra, giải pháp này có những ưu điểm sau:

  • IFS không bị ghi đè: Do phạm vi động của các biến cục bộ, việc ghi đè IFS qua một vòng lặp khiến giá trị mới bị rò rỉ vào các lệnh gọi hàm được thực hiện từ trong vòng lặp.

  • Mảng không được sử dụng: Đọc một chuỗi thành một mảng bằng cách sử dụng read yêu cầu cờ -a trong Bash và -A trong zsh.

Nếu muốn, chức năng có thể được đưa vào một tập lệnh như sau:

#!/usr/bin/env bash

split() {
    # ...
}

split "[email protected]"
8
Halle Knast

Có một cách đơn giản và thông minh như thế này:

echo "add:sfff" | xargs -d: -i  echo {}

Nhưng bạn phải sử dụng gnu xargs, BSD xargs không thể hỗ trợ -d delim. Nếu bạn sử dụng Apple mac như tôi. Bạn có thể cài đặt gnu xargs:

brew install findutils

sau đó

echo "add:sfff" | gxargs -d: -i  echo {}
7
Victor Choy

bạn có thể áp dụng awk cho nhiều tình huống

echo "[email protected];[email protected]"|awk -F';' '{printf "%s\n%s\n", $1, $2}'

bạn cũng có thể sử dụng cái này

echo "[email protected];[email protected]"|awk -F';' '{print $1,$2}' OFS="\n"
6
shuaihanhungry

Đây là cách đơn giản nhất để làm điều đó.

spo='one;two;three'
OIFS=$IFS
IFS=';'
spo_array=($spo)
IFS=$OIFS
echo ${spo_array[*]}
5
Arcabard

Có một số câu trả lời thú vị ở đây (errator đặc biệt

IN="[email protected];[email protected]"
declare -a a="(${IN/;/ })";

Bây giờ ${a[0]}, ${a[1]}, v.v., như bạn mong đợi. Sử dụng ${#a[*]} cho số lượng điều khoản. Hoặc lặp đi lặp lại, tất nhiên:

for i in ${a[*]}; do echo $i; done

LƯU Ý QUAN TRỌNG:

Điều này hoạt động trong trường hợp không có không gian để lo lắng, giải quyết vấn đề của tôi, nhưng có thể không giải quyết vấn đề của bạn. Đi với (các) giải pháp $IFS trong trường hợp đó.

4
eukras
IN="[email protected];[email protected]"
IFS=';'
read -a IN_arr <<< "${IN}"
for entry in "${IN_arr[@]}"
do
    echo $entry
done

Đầu ra

[email protected]
[email protected]

Hệ thống: Ubuntu 12.04.1

3
rashok

Hai lựa chọn thay thế bourne-ish trong đó không yêu cầu mảng bash:

Trường hợp 1 : Giữ cho nó đẹp và đơn giản: Sử dụng NewLine làm Dấu tách bản ghi ... ví dụ:.

IN="[email protected]
[email protected]"

while read i; do
  # process "$i" ... eg.
    echo "[email:$i]"
done <<< "$IN"

Lưu ý: trong trường hợp đầu tiên này, không có quy trình phụ nào được chia rẽ để hỗ trợ thao tác danh sách.

Ý tưởng: Có thể đáng để sử dụng NL rộng rãi bên trong và chỉ chuyển đổi sang RS khác khi tạo kết quả cuối cùng bên ngoài .

Trường hợp 2 : Sử dụng dấu ";" như một dấu phân tách hồ sơ ... ví dụ.

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"

Trong cả hai trường hợp, một danh sách phụ có thể được tạo trong vòng lặp vẫn tồn tại sau khi vòng lặp hoàn thành. Điều này hữu ích khi thao tác danh sách trong bộ nhớ, thay vì lưu trữ danh sách trong tệp. {p.s. giữ bình tĩnh và tiếp tục B-)}

2
NevilleDNZ

Nếu không có không gian, tại sao không?

IN="[email protected];[email protected]"
arr=(`echo $IN | tr ';' ' '`)

echo ${arr[0]}
echo ${arr[1]}
2
ghost

Ngoài những câu trả lời tuyệt vời đã được cung cấp, nếu đó chỉ là vấn đề in ra dữ liệu bạn có thể cân nhắc sử dụng awk:

awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "$IN"

Cái này đặt dấu phân cách trường thành ;, để nó có thể lặp qua các trường có vòng lặp for và in tương ứng.

Kiểm tra

$ IN="[email protected];[email protected]"
$ awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "$IN"
> [[email protected]]
> [[email protected]]

Với đầu vào khác:

$ awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "a;b;c   d;e_;f"
> [a]
> [b]
> [c   d]
> [e_]
> [f]
1
fedorqui

Được rồi các bạn!

Đây là câu trả lời của tôi!

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

Tại sao phương pháp này là "tốt nhất" đối với tôi?

Vì hai lý do:

  1. Bạn làm không cần phải thoát dấu phân cách;
  2. Bạn sẽ không có vấn đề với khoảng trắng . Giá trị sẽ được phân tách đúng trong mảng!

[]'S

1
Eduardo Lucio
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

Đầu ra:

[email protected]
[email protected]
Charlie Brown <[email protected]
!"#$%&/()[]{}*? are no problem
simple is beautiful :-)

Giải thích: Phép gán đơn giản sử dụng dấu ngoặc đơn () chuyển đổi danh sách được phân tách bằng dấu chấm phẩy thành một mảng với điều kiện bạn có IFS chính xác trong khi thực hiện điều đó. Vòng lặp FOR tiêu chuẩn xử lý các mục riêng lẻ trong mảng đó như bình thường. Lưu ý rằng danh sách được cung cấp cho biến IN phải được trích dẫn "cứng", nghĩa là với các dấu chọn đơn.

IFS phải được lưu và khôi phục vì Bash không coi một phép gán giống như một lệnh. Một cách giải quyết khác là bọc phần gán bên trong một hàm và gọi hàm đó bằng IFS đã sửa đổi. Trong trường hợp đó, việc lưu/khôi phục IFS riêng biệt là không cần thiết. Cảm ơn "Bize" đã chỉ ra điều đó.

1
ajaaskel

Trong Android Shell, hầu hết các phương pháp được đề xuất không hoạt động:

$ IFS=':' read -ra ADDR <<<"$PATH"                             
/system/bin/sh: can't create temporary file /sqlite_stmt_journals/mksh.EbNoR10629: No such file or directory

Công việc là gì:

$ for i in ${PATH//:/ }; do echo $i; done
/sbin
/vendor/bin
/system/sbin
/system/bin
/system/xbin

trong đó // có nghĩa là thay thế toàn cầu.

1
18446744073709551615

Sử dụng set tích hợp để tải lên mảng [email protected]:

IN="[email protected];[email protected]"
IFS=';'; set $IN; IFS=$' \t\n'

Sau đó, hãy để bữa tiệc bắt đầu:

echo $#
for a; do echo $a; done
ADDR1=$1 ADDR2=$2
1
jeberle

Có thể không phải là giải pháp tao nhã nhất, nhưng hoạt động với * và dấu cách:

IN="[email protected] me.com;*;[email protected]"
for i in `delims=${IN//[^;]}; seq 1 $((${#delims} + 1))`
do
   echo "> [`echo $IN | cut -d';' -f$i`]"
done

Đầu ra

> [[email protected] me.com]
> [*]
> [[email protected]]

Ví dụ khác (dấu phân cách ở đầu và cuối):

IN=";[email protected] me.com;*;[email protected];"
> []
> [[email protected] me.com]
> [*]
> [[email protected]]
> []

Về cơ bản, nó loại bỏ mọi ký tự khác ngoài ; tạo delims, vd. ;;;. Sau đó, nó lặp for từ 1 đến number-of-delimiters như được tính bởi ${#delims}. Bước cuối cùng là lấy phần $i một cách an toàn bằng cách sử dụng cut.

0
Petr Újezdský

Một lớp lót để phân tách một chuỗi được phân tách bằng ';' thành một mảng là:

IN="[email protected];[email protected]"
ADDRS=( $(IFS=";" echo "$IN") )
echo ${ADDRS[0]}
echo ${ADDRS[1]}

Điều này chỉ đặt IFS trong một mạng con, vì vậy bạn không phải lo lắng về việc lưu và khôi phục giá trị của nó.

0
Michael Hale

Điều này thậm chí sẽ xử lý khoảng trắng:

IFS=';' read ADDR1 ADDR2 <<< $(echo ${IN})
0
Mat Bess