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


2042

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

IN="bla@some.com;john@home.com"

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="bla@some.com"
ADDR2="john@home.com"

Tôi không nhất thiết cần ADDR1ADDR2các biến. 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="bla@some.com;john@home.com"

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

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

Đầu ra:

> [bla@some.com]
> [john@home.com]

Có một giải pháp liên quan đến việc đặ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 IFSlại về mặc định?

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

IN="bla@some.com;john@home.com"

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ỉ có chuỗi đầu tiên khi in nó trong vòng lặp, không có dấu ngoặc quanh $INnó hoạt động.


14
Liên quan đến "Edit2" của bạn: Bạn có thể chỉ cần "hủy đặt IFS" và nó sẽ trở về trạng thái mặc định. Không cần phải lưu và khôi phục nó một cách rõ ràng trừ khi bạn có một số lý do để mong đợi rằng nó đã được đặt thành giá trị không mặc định. Hơn nữa, nếu bạn đang thực hiện việc này bên trong một hàm (và, nếu bạn không, tại sao không?), Bạn có thể đặt IFS làm biến cục bộ và nó sẽ trở về giá trị trước đó khi bạn thoát khỏi hàm.
Brooks Moses

19
@BrooksMoses: (a) +1 để sử dụng local IFS=...khi có thể; (b) -1 cho unset IFS, điều này không chính xác IFS đặt lại giá trị mặc định của nó, mặc dù tôi tin rằng một IFS unset cư xử giống như các giá trị mặc định của IFS ($ '\ t \ n'), tuy nhiên có vẻ như thực tế xấu giả định một cách mù quáng rằng mã của bạn sẽ không bao giờ được gọi với IFS được đặt thành giá trị tùy chỉnh; (c) một ý tưởng khác là gọi một subshell: (IFS=$custom; ...)khi subshell thoát IFS sẽ trở về bất cứ thứ gì ban đầu.
dubiousjim

Tôi chỉ muốn có một cái nhìn nhanh chóng về các đường dẫn để quyết định nơi ném một thực thi, vì vậy tôi đã dùng đến để chạy ruby -e "puts ENV.fetch('PATH').split(':')". Nếu bạn muốn duy trì bash thuần thì sẽ không có ích gì nhưng sử dụng bất kỳ ngôn ngữ script nào có phân tách tích hợp sẽ dễ dàng hơn.
nicooga

4
for x in $(IFS=';';echo $IN); do echo "> [$x]"; done
dùng2037659

2
Để lưu nó thành một mảng, tôi phải đặt một dấu ngoặc đơn khác và thay đổi \nchỉ cho một khoảng trắng. Vì vậy, dòng cuối cùng là mails=($(echo $IN | tr ";" " ")). Vì vậy, bây giờ tôi có thể kiểm tra các yếu tố của mailsbằng cách sử dụng ký hiệu mảng mails[index]hoặc chỉ lặp trong một vòng lặp
afranques

Câu trả lời:


1236

Bạn có thể đặt biến phân tách trường bên trong (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 IFSchỉ diễn ra trong môi trường của lệnh đó (đến read). Sau đó, nó phân tích cú pháp đầu vào theo IFSgiá trị biến 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 cách ;đẩ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"

22
Đây có lẽ là cách tốt nhất. IFS sẽ tồn tại trong giá trị hiện tại của nó trong bao lâu, nó có thể làm rối mã của tôi bằng cách được đặt khi không nên và làm cách nào để đặt lại mã khi tôi hoàn thành?
Chris Lutz

7
bây giờ sau khi sửa lỗi được áp dụng, chỉ trong khoảng thời gian của lệnh đọc :)
Johannes Schaub - litb

14
Bạn có thể đọc mọi thứ cùng một lúc mà không cần sử dụng vòng lặp while: read -r -d '' -a addr <<< "$ in" # The -d '' là chìa khóa ở đây, nó cho biết đọc không dừng ở dòng mới đầu tiên ( là mặc định -d) nhưng để tiếp tục cho đến khi EOF hoặc byte NULL (chỉ xảy ra trong dữ liệu nhị phân).
lhunath

55
@LucaBorrione Thiết lập IFStrên cùng một dòng readvới không có dấu chấm phẩy hoặc dấu phân cách khác, trái với lệnh riêng biệt, phạm vi nó theo lệnh đó - vì vậy nó luôn được "khôi phục"; bạn không cần phải làm bất cứ điều gì bằng tay.
Charles Duffy

5
@imagineerĐây có một lỗi liên quan đến herestrings và các thay đổi cục bộ đối với IFS yêu cầu $INđược trích dẫn. Lỗi được sửa trong bash4.3.
chepner

973

Lấy từ mảng phân tách shell Bash :

IN="bla@some.com;john@home.com"
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 ';'(ban đầu //có nghĩa là thay thế toàn cầu) trong chuỗi INbằng ' '(một khoảng trắng), sau đó diễn giải chuỗi phân tách không gian thành một mảng (đó là những gì các dấu ngoặc đơn xung quanh làm).

Cú pháp được sử dụng bên trong các dấu ngoặc nhọn để thay thế mỗi ';'ký tự bằng một ' '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 gốc có khoảng trắng dấ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;

84
Tôi chỉ muốn thêm: đây là cách đơn giản nhất trong tất cả, bạn có thể truy cập các phần tử mảng bằng $ {ArrayIN [1]} (tất nhiên bắt đầu từ số không)
Oz123

26
Đã tìm thấy nó: kỹ thuật sửa đổi một biến trong $ {} được gọi là 'mở rộng tham số'.
KomodoDave

23
Không, tôi không nghĩ rằng nó hoạt động khi có cả không gian hiện diện ... nó chuyển đổi ',' thành '' và sau đó xây dựng một mảng được phân tách bằng không gian.
Ethan

12
Rất ngắn gọn, nhưng có những cảnh báo cho việc sử dụng chung : shell áp dụng phân tách từmở rộng cho chuỗi, có thể không mong muốn; Chỉ cần thử với. IN="bla@some.com;john@home.com;*;broken apart". Nói tóm lại: cách tiếp cận này sẽ bị phá vỡ, nếu mã thông báo của bạn chứa không gian nhúng và / hoặc ký tự. chẳng hạn như *điều đó xảy ra để tạo mã thông báo khớp với tên tệp trong thư mục hiện tại.
mkuity0

53
Đây là một cách tiếp cận tồi vì các lý do khác: Chẳng hạn, nếu chuỗi của bạn chứa ;*;, thì chuỗi *sẽ được mở rộng thành danh sách tên tệp trong thư mục hiện tại. -1
Charles Duffy

249

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ù.


Bạn nên giữ câu trả lời của IFS. Nó đã dạy tôi một điều mà tôi không biết, và nó chắc chắn đã tạo ra một mảng, trong khi điều này chỉ tạo ra một sự thay thế rẻ tiền.
Chris Lutz

Tôi hiểu rồi. Vâng tôi thấy làm những thí nghiệm ngớ ngẩn này, tôi sẽ học những điều mới mỗi lần tôi cố gắng trả lời mọi thứ. Tôi đã chỉnh sửa nội dung dựa trên #bash phản hồi IRC và không bị xóa :)
Johannes Schaub - litb

33
-1, rõ ràng là bạn không biết về việc viết từ, bởi vì nó giới thiệu hai lỗi trong mã của bạn. một là khi bạn không trích dẫn $ IN và hai là khi bạn giả vờ một dòng mới là dấu phân cách duy nhất được sử dụng trong từ ghép. Bạn đang lặp đi lặp lại trên mỗi WORD trong IN, không phải mọi dòng và DEFINATELY không phải mọi phần tử được giới hạn bởi dấu chấm phẩy, mặc dù nó có vẻ có tác dụng phụ trông giống như nó hoạt động.
lhunath

3
Bạn có thể thay đổi nó thành tiếng vang "$ IN" | tr ';' '\ N' | trong khi đọc -r THÊM; làm # xử lý "$ THÊM"; thực hiện để làm cho anh ta may mắn, tôi nghĩ :) Lưu ý rằng điều này sẽ rẽ nhánh và bạn không thể thay đổi các biến ngoài từ trong vòng lặp (đó là lý do tại sao tôi sử dụng cú pháp <<< "$ IN") sau đó
Johannes Schaub - litb

8
Để tóm tắt cuộc tranh luận trong các ý kiến: Hãy cẩn thận để sử dụng chung : trình bao áp dụng phân tách từmở rộng cho chuỗi, có thể không mong muốn; Chỉ cần thử với. IN="bla@some.com;john@home.com;*;broken apart". Nói tóm lại: cách tiếp cận này sẽ bị phá vỡ, nếu mã thông báo của bạn chứa không gian nhúng và / hoặc ký tự. chẳng hạn như *điều đó xảy ra để tạo mã thông báo khớp với tên tệp trong thư mục hiện tại.
mkuity0

202

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

Có rất nhiều cách khác nhau để làm điều này trong .

Tuy nhiên, điều quan trọng cần lưu ý đầu tiên là bashcó nhiều tính năng đặc biệt (được gọi là bashism ) sẽ không hoạt động trong bất kỳ tính năng nào khác.

Đặc biệt, mảng , mảng kết hợp , và thay thế mô hình , được sử dụng trong các giải pháp trong bài viết này cũng như những người khác trong các chủ đề, là bashisms và có thể không làm việc dưới khác vỏ mà nhiều người sử dụng.

Ví dụ: trên Debian GNU / Linux của tôi , có một vỏ tiêu chuẩn được gọi là; Tôi biết nhiều người thích sử dụng một vỏ khác được gọi là; và cũng có một công cụ đặc biệt gọi là với trình thông dịch shell của riêng mình ().

Chuỗi yêu cầu

Chuỗi được phân chia trong câu hỏi trên là:

IN="bla@some.com;john@home.com"

Tôi sẽ sử dụng một phiên bản sửa đổi của chuỗi này để đảm bảo rằng giải pháp của tôi mạnh mẽ đối với các chuỗi chứa khoảng trắng, có thể phá vỡ các giải pháp khác:

IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"

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

Trong sạch bash , chúng ta có thể tạo một mảng với các phần tử được phân tách bằng một giá trị tạm thời cho IFS ( dấu tách trường đầu vào ). IFS, trong số những thứ khác, cho biết bash(các) ký tự nào sẽ được coi là dấu phân cách giữa các phần tử khi xác định một mảng:

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

Trong các phiên bản mới hơn của bash, tiền tố một lệnh với một định nghĩa IFS thay đổi IFS cho lệnh đó chỉ và resets nó với giá trị trước đó ngay lập tức sau đó. Điều này có nghĩa là chúng ta có thể làm như trên chỉ trong một dòng:

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'

Chúng ta có thể thấy rằng chuỗi INđã được lưu trữ thành một mảng có tên fields, được phân chia trên dấu chấm phẩy:

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

(Chúng tôi cũng có thể hiển thị nội dung của các biến này bằng cách sử dụng 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>")

Lưu ý rằng đó readlà cách nhanh nhất để thực hiện phân tách vì không có nhánh hoặc tài nguyên bên ngoài được gọi.

Khi mảng được xác định, bạn có thể sử dụng một vòng lặp đơn giản để xử lý từng trường (hoặc, đúng hơn là từng phần tử trong mảng mà bạn đã xác định bây giờ):

# `"${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>]

Hoặc bạn có thể thả từng trường từ mảng sau khi xử lý bằng cách sử dụng phương pháp dịch chuyển , mà tôi thích:

while [ "$fields" ] ;do
    echo "> [$fields]"
    # slice the array 
    fields=("${fields[@]:1}")
    done
# > [bla@some.com]
# > [john@home.com]
# > [Full Name <fulnam@other.org>]

Và nếu bạn chỉ muốn một bản in đơn giản của mảng, bạn thậm chí không cần phải lặp lại nó:

printf "> [%s]\n" "${fields[@]}"
# > [bla@some.com]
# > [john@home.com]
# > [Full Name <fulnam@other.org>]

Cập nhật: gần đây > = 4,4

Trong các phiên bản mới hơn bash, bạn cũng có thể chơi với lệnh 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 muốn bao gồm các trường trống, bạn có thể làm như sau:

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

Với mapfile, bạn cũng có thể bỏ qua việc khai báo một mảng và ngầm "lặp" qua các phần tử được phân tách, gọi một hàm trên mỗi:

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

(Lưu ý: \0ở cuối chuỗi định dạng là vô ích nếu bạn không quan tâm đến các trường trống ở cuối chuỗi hoặc chúng không có mặt.)

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.

Hoặc bạn có thể sử dụng <<<và trong thân hàm bao gồm một số xử lý để bỏ dòng mới, nó thêm vào:

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.

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

Nếu bạn không thể sử dụng bashhoặc nếu bạn muốn viết một cái gì đó có thể được sử dụng trong nhiều shell khác nhau, bạn thường không thể sử dụng bashism - và điều này bao gồm các mảng chúng tôi đã sử dụng trong các giải pháp ở trên.

Tuy nhiên, chúng ta không cần sử dụng mảng để lặp lại "các phần tử" của chuỗi. Có một cú pháp được sử dụng trong nhiều shell để xóa các chuỗi con của chuỗi từ lần xuất hiện đầu tiên hoặc lần cuối của mẫu. Lưu ý rằng đó *là ký tự đại diện cho không hoặc nhiều ký tự:

(Việc thiếu cách tiếp cận này trong bất kỳ giải pháp nào được đăng cho đến nay là lý do chính khiến tôi viết câu trả lời này;)

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

Theo giải thích của Score_Under :

#%xóa chuỗi con phù hợp ngắn nhất có thể từ đầucuối chuỗi tương ứng, và

##%%xóa chuỗi con phù hợp dài nhất có thể.

Sử dụng cú pháp trên, chúng ta có thể tạo một cách tiếp cận trong đó chúng ta trích xuất các "phần tử" của chuỗi con khỏi chuỗi bằng cách xóa các chuỗi con lên đến hoặc sau dấu phân cách.

Các codeblock dưới đây hoạt động tốt trong (bao gồm cả Mac OS bash),, 'S :

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

Chúc vui vẻ!


15
Các #, ##, %, và %%thay thế có gì là IMO một lời giải thích dễ nhớ (ví bao nhiêu họ xóa): #%xóa chuỗi khớp ngắn nhất có thể, và ##%%xóa có thể dài nhất.
Điểm_Under

1
Các IFS=\; read -a fields <<<"$var"thất bại trên dòng mới và thêm một ký tự dòng mới. Các giải pháp khác loại bỏ một trường trống trailing.
Isaac

Dấu phân cách vỏ là câu trả lời tao nhã nhất, thời kỳ.
Eric Chen

Có thể sử dụng thay thế cuối cùng với một danh sách các dấu tách trường được đặt ở một nơi khác không? Chẳng hạn, ý tôi là sử dụng tập lệnh này làm tập lệnh shell và truyền danh sách các dấu tách trường làm tham số vị trí.
sancho.s RebstateMonicaCellio

Có, trong một vòng lặp:for sep in "#" "ł" "@" ; do ... var="${var#*$sep}" ...
F. Hauri

184

Tôi đã thấy một vài câu trả lời tham khảo cutlệnh, 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 bash script, trcó thể hiệu quả hơn, nhưng cutcó thể được sử dụng 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 "bla@some.com;john@home.com" | cut -d ";" -f 1
bla@some.com
$ echo "bla@some.com;john@home.com" | cut -d ";" -f 2
john@home.com

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

cutrất thuận tiện để có thể vào cattập tin này và chọn một trường cụ thể để xử lý thêm.


6
Kudos để sử dụng cut, đó là công cụ phù hợp cho công việc! Xóa nhiều hơn bất kỳ hack nào.
MisterMiyagi

4
Cách tiếp cận này sẽ chỉ hoạt động nếu bạn biết trước số lượng phần tử; bạn cần lập trình thêm một số logic xung quanh nó. Nó cũng chạy một công cụ bên ngoài cho mọi yếu tố.
uli42

Thật thú vị khi tôi đang cố gắng tránh chuỗi trống trong một csv. Bây giờ tôi cũng có thể chỉ ra giá trị 'cột' chính xác. Làm việc với IFS đã được sử dụng trong một vòng lặp. Tốt hơn so với dự kiến ​​cho tình huống của tôi.
Louis Loudog Trottier

Rất hữu ích để lấy ID và PID, tức là
Milos Grujic

Câu trả lời này đáng để cuộn xuống hơn nửa trang :)
Gucu112

124

Đ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

1
Mặc dù nó chỉ hoạt động với một dấu phân cách ký tự duy nhất, đó là những gì OP đang tìm kiếm (các bản ghi được phân định bằng dấu chấm phẩy).
GuyPaddock

Đã trả lời khoảng bốn năm trước bởi @Ashok , và cũng, hơn một năm trước bởi @DougW , hơn câu trả lời của bạn, với nhiều thông tin hơn. Vui lòng gửi giải pháp khác với những người khác.
MAChitgarha

90

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

IN="bla@some.com;john@home.com" 
set -- "$IN" 
IFS=";"; declare -a Array=($*) 
echo "${Array[@]}" 
echo "${Array[0]}" 
echo "${Array[1]}" 

Nguồn


7
+1 ... nhưng tôi sẽ không đặt tên biến là "Mảng" ... pet peev tôi đoán. Giải pháp tốt.
Yzmir Ramirez

14
+1 ... nhưng "thiết lập" và khai báo -a là không cần thiết. Bạn cũng có thể đã sử dụng chỉIFS";" && Array=($IN)
ata

+1 Chỉ có một lưu ý phụ: không nên khuyến khích giữ IFS cũ và sau đó khôi phục nó? (như thể hiện bởi stefanB trong EDIT3 mình) người hạ cánh ở đây (đôi khi chỉ cần sao chép và dán một giải pháp) có thể không nghĩ về điều này
Luca Borrione

6
-1: Đầu tiên, @ata đúng rằng hầu hết các lệnh trong này không làm gì cả. Thứ hai, nó sử dụng phân tách từ để tạo thành mảng và không làm gì để ngăn chặn sự mở rộng toàn cầu khi làm như vậy (vì vậy nếu bạn có các ký tự toàn cầu trong bất kỳ thành phần mảng nào, các thành phần đó được thay thế bằng tên tệp phù hợp).
Charles Duffy

1
Đề nghị sử dụng $'...': IN=$'bla@some.com;john@home.com;bet <d@\ns* kl.com>'. Sau đó echo "${Array[2]}"sẽ in một chuỗi với dòng mới. set -- "$IN"cũng không cần thiết trong trường hợp này. Có, để ngăn chặn sự mở rộng toàn cầu, giải pháp nên bao gồm set -f.
John_West

79

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 theo mặc định trong hầu hết mọi bản phân phối Linux.

echo "bla@some.com;john@home.com" | awk -F';' '{print $1,$2}'

sẽ cho

bla@some.com john@home.com

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.


3
Hoặc thậm chí đơn giản hơn: echo "bla@some.com; john@home.com" | awk 'BEGIN {RS = ";"} {print}'
Jaro

@Jaro Điều này hoạt động hoàn hảo với tôi khi tôi có một chuỗi bằng dấu phẩy và cần định dạng lại nó thành các dòng. Cảm ơn.
Aquarelle

Nó hoạt động trong kịch bản này -> "echo" $ SPLIT_0 "| awk -F 'inode =' '{print $ 1}'"! Tôi gặp vấn đề khi cố gắng sử dụng atrings ("inode =") thay vì ký tự (";"). $ 1, $ 2, $ 3, $ 4 được đặt làm vị trí trong một mảng! Nếu có cách thiết lập một mảng ... tốt hơn! Cảm ơn!
Eduardo Lucio

@EduardoLucio, điều tôi nghĩ đến là có thể trước tiên bạn có thể thay thế dấu phân cách của mình inode=thành ;ví dụ bằng cách sed -i 's/inode\=/\;/g' your_file_to_processxác định -F';'khi áp dụng awk, hy vọng điều đó có thể giúp bạn.
Tống

66
echo "bla@some.com;john@home.com" | sed -e 's/;/\n/g'
bla@some.com
john@home.com

4
-1 nếu chuỗi chứa khoảng trắng thì sao? ví dụ IN="this is first line; this is second line" arrIN=( $( echo "$IN" | sed -e 's/;/\n/g' ) )sẽ tạo ra một mảng gồm 8 phần tử trong trường hợp này (một phần tử cho mỗi không gian từ được phân tách), chứ không phải là 2 (một phần tử cho mỗi dòng dấu hai chấm được phân tách)
Luca Borrione

3
@Luca Không có kịch bản sed tạo chính xác hai dòng. Điều tạo ra nhiều mục nhập cho bạn là khi bạn đặt nó vào một mảng bash (phân chia theo khoảng trắng theo mặc định)
lothar

Đó chính xác là vấn đề: OP cần lưu trữ các mục vào một mảng để lặp qua nó, như bạn có thể thấy trong các chỉnh sửa của mình. Tôi nghĩ rằng câu trả lời (tốt) của bạn đã bỏ lỡ để đề cập đến để sử dụng arrIN=( $( echo "$IN" | sed -e 's/;/\n/g' ) )để đạt được điều đó và để khuyên nên thay đổi IFS IFS=$'\n'cho những người đến đây trong tương lai và cần chia một chuỗi chứa khoảng trắng. (và để khôi phục lại sau đó). :)
Luca Borrione

1
@Luca Điểm tốt. Tuy nhiên, việc gán mảng không nằm trong câu hỏi ban đầu khi tôi viết lên câu trả lời đó.
lothar

65

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

IN="bla@some.com;john@home.com"
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 "bla@some.com", nó sẽ chỉ định nó cho cả ADD1 và ADD2.


1
Bạn có thể sử dụng -s để tránh sự cố đã đề cập: superuser.com/questions/896800/ Khăn "-f, --fields = LIST chỉ chọn các trường này, cũng in bất kỳ dòng nào không chứa ký tự dấu phân cách, trừ khi tùy chọn -s được chỉ định "
fersarr 3/03/2016

34

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="bla@some.com;john@home.com"
read ADDR1 ADDR2 <<<$(IFS=";"; echo $IN)

Tôi nghĩ rằng nó làm! Chạy các lệnh ở trên và sau đó "echo $ ADDR1 ... $ ADDR2" và tôi nhận được "bla@some.com ... john@home.com" đầu ra
nickjb

1
Điều này thực sự hiệu quả với tôi ... Tôi đã sử dụng nó để lặp lại một chuỗi các chuỗi chứa dữ liệu DB, SERVER, PORT được phân tách bằng dấu phẩy để sử dụng mysqldump.
Nick

5
Chẩn đoán: sự IFS=";"phân công chỉ tồn tại trong $(...; echo $IN)lớp con; đây là lý do tại sao một số độc giả (bao gồm cả tôi) ban đầu nghĩ rằng nó sẽ không hoạt động. Tôi giả định rằng tất cả $ IN đang bị ADDR1 làm lu mờ. Nhưng nickjb là chính xác; nó làm việc Lý do là echo $INlệnh phân tích cú pháp các đối số của nó bằng cách sử dụng giá trị hiện tại của $ IFS, nhưng sau đó lặp lại chúng thành thiết bị xuất chuẩn bằng cách sử dụng dấu phân cách không gian, bất kể cài đặt của $ IFS. Vì vậy, hiệu ứng ròng như thể người ta đã gọi read ADDR1 ADDR2 <<< "bla@some.com john@home.com"(lưu ý rằng đầu vào được phân tách bằng dấu cách; không tách biệt).
dubiousjim

1
Điều này thất bại trên không gian và dòng mới, và cũng mở rộng các ký tự đại diện *trong echo $INvới một mở rộng biến không được trích dẫn.
Isaac

Tôi thực sự thích giải pháp này. Một mô tả về lý do tại sao nó hoạt động sẽ rất hữu ích và làm cho nó một câu trả lời tổng thể tốt hơn.
Michael Gaskill

32

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 -dtùy chọn read(dấu phân cách) với một dấu phân cách trống, do đó readbuộc phải đọc mọi thứ nó được cho ăn. Và chúng tôi cung cấp readchính xác nội dung của biến in, không có dòng mới nhờ printf. Lưu ý rằng chúng tôi cũng đang đặt dấu phân cách printfđể đảm bảo rằng chuỗi được truyền đến readcó dấu phân cách. Không có nó, readsẽ cắt các trường trống tiềm năng:

$ 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, nội dung dựng sẵn mapfile(aka readarray) hỗ trợ -dtùy chọn chỉ định dấu phân cách. Do đó một cách kinh điển khác là:

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

5
Tôi thấy đó là giải pháp hiếm hoi trong danh sách đó hoạt động chính xác \n, không gian và *đồng thời. Ngoài ra, không có vòng lặp; biến mảng có thể truy cập được trong shell sau khi thực hiện (trái với câu trả lời được nâng cao nhất). Lưu ý, in=$'...'nó không hoạt động với dấu ngoặc kép. Tôi nghĩ rằng, nó cần nhiều hơn nữa.
John_West

28

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

Xem xét việc sử dụng read -r ...để đảm bảo rằng, ví dụ, hai ký tự "\ t" trong đầu vào kết thúc là hai ký tự giống nhau trong các biến của bạn (thay vì một ký tự tab đơn).
dubiousjim

-1 Điều này không hoạt động ở đây (ubfox 12.04). Thêm echo "ADDR1 $ADDR1"\n echo "ADDR2 $ADDR2"vào đoạn trích của bạn sẽ xuất ra ADDR1 bla@some.com john@home.com\nADDR2(\ n là dòng mới)
Luca Borrione

Điều này có thể là do một lỗi liên quan IFSvà ở đây các chuỗi đã được sửa trong bash4.3. Trích dẫn $INnên sửa nó. (Về lý thuyết, $INkhông phải chịu sự chia tách hoặc tách từ sau khi nó mở rộng, có nghĩa là các trích dẫn là không cần thiết. Ngay cả trong 4.3, tuy nhiên, vẫn còn ít nhất một lỗi còn lại - được báo cáo và lên lịch để sửa chữa - vì vậy trích dẫn vẫn còn tốt ý tưởng.)
chepner

Điều này phá vỡ nếu $ in chứa dòng mới ngay cả khi $ IN được trích dẫn. Và thêm một dòng mới.
Isaac

Một vấn đề với điều này, và nhiều giải pháp khác cũng cho rằng nó có các yếu tố CHÍNH XÁC trong $ IN - HOẶC rằng bạn sẵn sàng để các mục thứ hai và tiếp theo được đập tan trong ADDR2. Tôi hiểu rằng điều này đáp ứng yêu cầu, nhưng đó là một quả bom hẹn giờ.
Steven dễ dàng thích thú

22

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

20

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

in="foo@bar;bizz@buzz;fizz@buzz;buzz@woof"
IFS=';' list=($in)
for item in "${list[@]}"; do echo $item; done

trong đó IFSphâ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ả lại mỗi mục dưới dạng một từ riêng biệt.

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


5
Việc sử dụng không $intrích dẫn cho phép các ký tự đại diện được mở rộng.
Isaac

10

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:

  • IFSkhông bị ghi đè: Do phạm vi động của các biến cục bộ, việc ghi đè IFSlên một vòng lặp làm cho giá trị mới bị rò rỉ vào các lệnh gọi hàm được thực hiện từ bên 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 readcờ -atrong Bash và -Atrong 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 "$@"

Dường như không hoạt động với các dấu phân cách dài hơn 1 ký tự: split = $ (split "$ content" "file: //")
madprops

Đúng - từ help read:-d delim continue until the first character of DELIM is read, rather than newline
Halle Knast

8

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

echo "bla@some.com;john@home.com"|awk -F';' '{printf "%s\n%s\n", $1, $2}'

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

echo "bla@some.com;john@home.com"|awk -F';' '{print $1,$2}' OFS="\n"

7

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

4

Đâ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[*]}

4

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

IN="bla@some.com;john@home.com"
declare -a a="(${IN/;/ })";

Bây giờ ${a[0]}, ${a[1]}vv, như bạn mong đợi. Sử dụng ${#a[*]}cho số lượng các đ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 được vấn đề của bạn. Đi với $IFS(các) giải pháp trong trường hợp đó.


Không hoạt động khi INchứa nhiều hơn hai địa chỉ email. Vui lòng tham khảo cùng một ý tưởng (nhưng đã được sửa) tại câu trả lời của palindrom
olibre

Sử dụng tốt hơn ${IN//;/ }(dấu gạch chéo kép) để làm cho nó cũng hoạt động với nhiều hơn hai giá trị. Coi chừng mọi ký tự đại diện ( *?[) sẽ được mở rộng. Và một trường trống kéo dài sẽ bị loại bỏ.
Isaac

3
IN="bla@some.com;john@home.com"
IFS=';'
read -a IN_arr <<< "${IN}"
for entry in "${IN_arr[@]}"
do
    echo $entry
done

Đầu ra

bla@some.com
john@home.com

Hệ thống: Ubuntu 12.04.1


IFS không được thiết lập trong bối cảnh cụ thể readở đây và do đó nó có thể làm đảo lộn phần còn lại của mã, nếu có.
tiền mã hóa

2

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

IN="bla@some.com;john@home.com"
arr=(`echo $IN | tr ';' ' '`)

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

2

Sử dụng tích sethợp để tải lên $@mảng:

IN="bla@some.com;john@home.com"
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

Sử dụng tốt hơn set -- $INđể tránh một số vấn đề với "$ IN" bắt đầu bằng dấu gạch ngang. Tuy nhiên, việc mở rộng không được trích dẫn $INsẽ mở rộng ký tự đại diện ( *?[).
Isaac

2

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="bla@some.com
john@home.com"

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 trong nội bộ và chỉ chuyển đổi sang RS khác khi tạo kết quả cuối cùng ra 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="bla@some.com;john@home.com"
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ào đó lưu trữ danh sách trong tệp. {ps giữ bình tĩnh và tiếp tục B-)}


2

Ngoài các 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ể xem xét sử dụng awk:

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

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

Kiểm tra

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

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]

2

Trong Android shell, hầu hết các phương thức đượ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
Thất bại nếu bất kỳ phần nào của $ PATH chứa khoảng trắng (hoặc dòng mới). Đồng thời mở rộng các ký tự đại diện (dấu hoa thị *, dấu hỏi? Và dấu ngoặc []].
Isaac

2
IN='bla@some.com;john@home.com;Charlie Brown <cbrown@acme.com;!"#$%&/()[]{}*? 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:

bla@some.com
john@home.com
Charlie Brown <cbrown@acme.com
!"#$%&/()[]{}*? 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 nhiệm vụ 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 đó.


!"#$%&/()[]{}*? are no problemtốt ... không hoàn toàn: []*?là các nhân vật toàn cầu. Vì vậy, những gì về việc tạo thư mục và tệp này: đơn giản có thể là đẹp, nhưng khi nó bị hỏng, nó bị hỏng.
gniourf_gniourf

@gniourf_gniourf Chuỗi được lưu trữ trong một biến. Xin vui lòng xem câu hỏi ban đầu.
ajaaskel

1
@ajaaskel bạn không hiểu hết ý kiến ​​của tôi. Đi vào một thư mục đầu và đưa ra các lệnh sau : mkdir '!"#$%&'; touch '!"#$%&/()[]{} got you hahahaha - are no problem'. Họ sẽ chỉ tạo một thư mục và một tập tin, với những cái tên kỳ lạ, tôi phải thừa nhận. Sau đó chạy các lệnh của bạn với chính xác INbạn đã đưa ra : IN='bla@some.com;john@home.com;Charlie Brown <cbrown@acme.com;!"#$%&/()[]{}*? are no problem;simple is beautiful :-)'. Bạn sẽ thấy rằng bạn sẽ không nhận được đầu ra mà bạn mong đợi. Bởi vì bạn đang sử dụng một chủ đề phương thức để mở rộng tên đường dẫn để phân tách chuỗi của bạn.
gniourf_gniourf

Điều này là để chứng minh rằng các nhân vật *, ?, [...]và thậm chí, nếu extglobđược thiết lập, !(...), @(...), ?(...), +(...) vấn đề với phương pháp này!
gniourf_gniourf

1
@gniourf_gniourf Cảm ơn bạn đã nhận xét chi tiết về Globing. Tôi đã điều chỉnh mã để tắt. Tuy nhiên, quan điểm của tôi là chỉ ra rằng việc phân công khá đơn giản có thể thực hiện công việc chia tách.
ajaaskel

1

Đượ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 không cần phải thoát khỏi dấu phân cách;
  2. Bạn sẽ không gặp vấn đề với các khoảng trống . Giá trị sẽ được phân tách đúng trong mảng!

[]'S


FYI, /etc/os-release/etc/lsb-releasecó nghĩa là có nguồn gốc, và không được phân tích cú pháp. Vì vậy, phương pháp của bạn là thực sự sai. Hơn nữa, bạn không hoàn toàn trả lời câu hỏi về việc làm xáo trộn một chuỗi trên một dấu phân cách.
gniourf_gniourf

0

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

IN="bla@some.com;john@home.com"
ADDRS=( $(IFS=";" echo "$IN") )
echo ${ADDRS[0]}
echo ${ADDRS[1]}

Điều này chỉ đặt IFS trong một nhánh 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ó.


-1 cái này không hoạt động ở đây (ubfox 12.04). nó chỉ in ra tiếng vang đầu tiên với tất cả giá trị $ IN trong đó, trong khi lần thứ hai trống. bạn có thể thấy nó nếu bạn đặt echo "0:" $ {ADDRS [0]} \ n echo "1:" $ {ADDRS [1]} đầu ra là 0: bla@some.com;john@home.com\n 1:(\ n là dòng mới)
Luca Borrione

1
vui lòng tham khảo câu trả lời của nickjb tại để thay thế cho ý tưởng này stackoverflow.com/a/6583589/1032370
Luca Borrione

1
-1, 1. IFS không được đặt trong mạng con đó (nó được truyền vào môi trường của "echo", là một nội trang, vì vậy dù sao cũng không có gì xảy ra). 2. $INđược trích dẫn để nó không bị chia tách IFS. 3. Sự thay thế quá trình được phân chia bởi khoảng trắng, nhưng điều này có thể làm hỏng dữ liệu gốc.
Điểm_Under

0

Có thể không phải là giải pháp thanh lịch nhất, nhưng hoạt động với *và không gian:

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

Đầu ra

> [bla@so me.com]
> [*]
> [john@home.com]

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

IN=";bla@so me.com;*;john@home.com;"
> []
> [bla@so me.com]
> [*]
> [john@home.com]
> []

Về cơ bản, nó loại bỏ mọi nhân vật khác hơn là ;làm delimsví dụ. ;;;. Sau đó, nó forlặp từ 1đến number-of-delimitersnhư được tính bởi ${#delims}. Bước cuối cùng là $isử dụng phần thứ một một cách an toàn cut.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.