Làm cách nào để tạo một mảng các phần tử duy nhất từ ​​một chuỗi / mảng trong bash?


8

Nếu tôi có một chuỗi "1 2 3 2 1" - hoặc một mảng [1,2,3,2,1] - làm cách nào tôi có thể chọn các giá trị duy nhất, nghĩa là

"1 2 3 2 1" produces "1 2 3" 

hoặc là

[1,2,3,2,1] produces [1,2,3]

Tương tự như uniq nhưng uniq dường như hoạt động trên toàn bộ các dòng, không phải các mẫu trong một dòng ...

Câu trả lời:


4

Với GNU awk(điều này cũng giữ nguyên thứ tự ban đầu)

printf '%s\n' "1 2 3 2 1" | awk -v RS='[[:space:]]+' '!a[$0]++{printf "%s%s", $0, RT}'
1 2 3 

Để readvào một bashmảng

read -ra arr<<<$(printf '%s\n' "1 2 3 2 1" |
 awk -v RS='[[:space:]]+' '!a[$0]++{printf "%s%s", $0, RT}')
printf "%s\n"  "${arr[@]}"
1
2
3

Làm thế nào tôi có thể làm cho một mảng?
Michael Durrant

@MichaelDurrant, nếu bạn muốn nói đến một bashmảng, đã thêm một cách
iruvar

Xem ở đây nếu mảng của bạn chứa khoảng trắng
Tom Hale

@iruvar bạn có thể vui lòng giải thích điều này thực sự có nghĩa là gì không? Tôi chưa quen với kịch bản awk và sẽ rất hữu ích nếu bạn có thể làm rõ những gì thực sự xảy ra khi bạn nói điều này! [$ 0] ++
Abhishek

@iruvar nếu không thể giải thích trong các bình luận thì bất kỳ trang web nào giải thích cú pháp trên ít nhất sẽ có lợi.
Abhishek

9

Nếu bạn đang sử dụng zsh:

$ array=(1 2 3 2 1)
$ echo ${(u)array[@]}
1 2 3

hoặc (nếu KSH_ARRAYStùy chọn không được đặt) thậm chí

$ echo ${(u)array}
1 2 3

1
Nếu mảng có thể chứa các phần tử trống, bạn nên sử dụng "${(u)array[@]}"hoặc "${(@u)array}"thay vào đó (lưu ý các trích dẫn).
Stéphane Chazelas

Tôi đang sử dụng zsh 5.1.1 (x86_64-ubfox-linux-gnu)${(u)array}hoạt động ngay cả khi mảng trống hoặc chứa một chuỗi trống, không có dấu ngoặc kép.
kiamlaluno

4

Đối với một mảng có các giá trị tùy ý, nó khá phức tạp bashvì nó không có toán tử dựng sẵn cho điều đó.

bash tuy nhiên xảy ra không hỗ trợ lưu trữ các ký tự NUL trong các biến của nó, vì vậy bạn có thể sử dụng điều đó để chuyển điều đó sang các lệnh khác:

Tương đương với zsh:

new_array=("${(@u}array}")

trên một hệ thống GNU gần đây, có thể là:

eval "new_array=($(
  printf "%s\0" "${array[@]}" |
    LC_ALL=C sort -zu |
    xargs -r0 bash -c 'printf "%q\n" "$@"' sh
  ))"

Ngoài ra, với các phiên bản gần đây bashvà giả sử không có phần tử mảng nào trống, bạn có thể sử dụng mảng kết hợp:

unset hash
typeset -A hash
for i in "${array[@]}"; do
  hash[$i]=
done
new_array=("${!hash[@]}")

Với bash 4.4 trở lên và với GNU sort:

readarray -td '' new_array < <(
  printf '%s\0' "${array[@]}" | LC_ALL=C sort -zu)

Thứ tự của các yếu tố sẽ không giống nhau trong các giải pháp khác nhau.

Với tcsh:

set -f new_array = ($array:q)

Sẽ giữ lại các f yếu tố IRST ( a b a=> a b) như zsh's (u)cờ mở rộng.

set -l new_array = ($array:q)

Sẽ giữ lại lần cuối ( a b a=> b a). Tuy nhiên, những người loại bỏ các phần tử trống từ mảng.


1

Giải pháp này đã làm việc cho tôi.

ids=(1 2 3 2 1)
echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '

Ở trên tạo ra 1 2 3 là đầu ra.

Phiên bản ngắn hơn theo đề xuất của Costas có thể là,

printf "%s\n" "${ids[@]}" | sort -u | tr '\n' ' '

Để lưu trữ kết quả cuối cùng vào một mảng, bạn có thể làm một cái gì đó như,

IFS=$' '
arr=($(printf "%s\n" "${ids[@]}" | sort -u | tr '\n' ' '))
unset IFS

Bây giờ, khi tôi thực hiện một tiếng vang trên arr, đây là đầu ra tôi nhận được.

echo "${arr[@]}"
1 2 3

Người giới thiệu

https://stackoverflow.com/a/13648438/1742825 https://stackoverflow.com/a/9449633/1742825


@Costas, cảm ơn. Tôi đã kết hợp nó để trả lời.
Ramesh

Làm thế nào tôi có thể làm cho kết quả cuối cùng là một mảng?
Michael Durrant

@MichaelDurrant, vui lòng xem câu trả lời cập nhật và cho tôi biết nếu điều này tốt.
Ramesh

Nếu bạn muốn đưa kết quả vào mảng, bạn có thể loại bỏ lệnh cuối cùngtr '\n' ' '
Costas

0

Để làm điều đó hoàn toàn trong trình bao và đặt kết quả vào một mảng,

declare -A seen
for word in one two three two one
do
        if [ ! "${seen[$word]}" ]
        then
                result+=("$word")
                seen[$word]=1
        fi
done
echo "${result[@]}"

Trong các từ: nếu chúng ta chưa thấy một từ nào, hãy thêm nó vào resultmảng và đánh dấu nó như đã thấy. Một khi một từ đã được nhìn thấy, bỏ qua sự xuất hiện tiếp theo của nó.


2
Lưu ý rằng bạn cần unset seentrước đó declare -A seentrong trường hợp $seenđã được xác định trước đó (ngay cả dưới dạng biến vô hướng từ môi trường).
Stéphane Chazelas
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.