Chọn các giá trị duy nhất hoặc khác biệt từ một danh sách trong tập lệnh shell UNIX


238

Tôi có một tập lệnh ksh trả về một danh sách dài các giá trị, dòng mới được phân tách và tôi chỉ muốn xem các giá trị duy nhất / khác biệt. Có thể làm điều này?

Ví dụ: giả sử đầu ra của tôi là hậu tố tệp trong một thư mục:

tar
gz
java
gz
java
tar
class
class

Tôi muốn xem một danh sách như:

tar
gz
java
class

Câu trả lời:


431

Bạn có thể muốn xem xét uniqsortcác ứng dụng.

./yourscript.ksh | sắp xếp | uniq

(FYI, vâng, sắp xếp là cần thiết trong dòng lệnh này, uniqchỉ loại bỏ các dòng trùng lặp ngay sau nhau)

BIÊN TẬP:

Trái ngược với những gì đã được đăng bởi Aaron Digulla liên quan đến uniqcác tùy chọn dòng lệnh:

Cho đầu vào sau:

lớp học
bình
bình
bình
thùng rác
thùng rác
java

uniq sẽ xuất tất cả các dòng chính xác một lần:

lớp học
bình
thùng rác
java

uniq -d sẽ xuất ra tất cả các dòng xuất hiện nhiều lần và nó sẽ in chúng một lần:

bình
thùng rác

uniq -u sẽ xuất ra tất cả các dòng xuất hiện chính xác một lần và nó sẽ in chúng một lần:

lớp học
java

2
Chỉ là một FYI cho người đến sau: Câu trả lời của @ AaronDigulla đã được sửa chữa.
mkuity0

2
điểm rất hay này `sắp xếp là cần thiết trong dòng lệnh này, uniq chỉ loại bỏ các dòng trùng lặp ngay sau nhau 'mà tôi vừa học được !!
HattrickNZ

4
GNU sortcó một -uphiên bản để cung cấp các giá trị duy nhất.
Arthur2e5

Tôi đã tìm ra rằng uniqcác đường nối chỉ xử lý các đường liền kề (ít nhất là theo mặc định) có nghĩa là đường nối có thể được sortnhập trước khi cho ăn uniq.
Stphane

85
./script.sh | sort -u

Điều này giống như câu trả lời của monoxide , nhưng ngắn gọn hơn một chút.


6
Bạn đang khiêm tốn: giải pháp của bạn cũng sẽ hoạt động tốt hơn (có thể chỉ đáng chú ý với các tập dữ liệu lớn).
mkuity0

Tôi nghĩ rằng nó sẽ hiệu quả hơn ... | sort | uniqvì nó được thực hiện trong một lần bắn
Adrian Antunez

10

Đối với các tập dữ liệu lớn hơn mà việc sắp xếp có thể không mong muốn, bạn cũng có thể sử dụng tập lệnh perl sau:

./yourscript.ksh | perl -ne 'if (!defined $x{$_}) { print $_; $x{$_} = 1; }'

Điều này về cơ bản chỉ cần nhớ mỗi đầu ra dòng để nó không xuất ra lại.

Nó có lợi thế hơn sort | uniqgiải pháp "" ở chỗ không cần sắp xếp trước.


2
Lưu ý rằng việc sắp xếp một tệp rất lớn không phải là vấn đề với sắp xếp; nó có thể sắp xếp các tệp lớn hơn RAM + trao đổi. Perl, OTOH, sẽ thất bại nếu chỉ có một vài bản sao.
Aaron Digulla

1
Có, đó là một sự đánh đổi tùy thuộc vào dữ liệu dự kiến. Perl tốt hơn cho bộ dữ liệu khổng lồ với nhiều bản sao (không yêu cầu lưu trữ dựa trên đĩa). Tập dữ liệu khổng lồ với một vài bản sao nên sử dụng sort (và lưu trữ đĩa). Bộ dữ liệu nhỏ có thể sử dụng một trong hai. Cá nhân, tôi sẽ thử Perl trước, chuyển sang sắp xếp nếu thất bại.
paxdiablo

Vì sắp xếp chỉ mang lại cho bạn một lợi ích nếu nó phải trao đổi vào đĩa.
paxdiablo

5
Điều này thật tuyệt khi tôi muốn sự xuất hiện đầu tiên của mỗi dòng. Sắp xếp sẽ phá vỡ điều đó.
Bluu

10

Với zsh bạn có thể làm điều này:

% cat infile 
tar
more than one word
gz
java
gz
java
tar
class
class
zsh-5.0.0[t]% print -l "${(fu)$(<infile)}"
tar
more than one word
gz
java
class

Hoặc bạn có thể sử dụng AWK:

% awk '!_[$0]++' infile    
tar
more than one word
gz
java
class

2
Các giải pháp thông minh không liên quan đến việc sắp xếp đầu vào. Hãy cẩn thận: Giải pháp rất thông minh nhưng khó hiểu awk(xem stackoverflow.com/a/21200722/45375 để được giải thích) sẽ hoạt động với các tệp lớn miễn là số lượng dòng duy nhất đủ nhỏ (vì các dòng duy nhất được giữ trong bộ nhớ ). Các zshgiải pháp đọc toàn bộ tập tin vào bộ nhớ đầu tiên, mà có thể không là một lựa chọn với các tập tin lớn. Ngoài ra, như đã viết, chỉ các dòng không có không gian nhúng được xử lý chính xác; để khắc phục điều này, sử dụng IFS=$'\n' read -d '' -r -A u <file; print -l ${(u)u}thay thế.
mkuity0

Chính xác. Hoặc:(IFS=$'\n' u=($(<infile)); print -l "${(u)u[@]}")
Dimitre Radoulov

1
Cảm ơn, điều đó đơn giản hơn (giả sử bạn không cần đặt các biến cần thiết bên ngoài lớp con). Tôi tò mò khi bạn cần [@]hậu tố để tham chiếu tất cả các yếu tố của một mảng - dường như - ít nhất là với phiên bản 5 - nó hoạt động mà không có nó; hoặc bạn chỉ cần thêm nó cho rõ ràng?
mkuity0

1
@ mkuity0, bạn nói đúng! Tôi đã không nghĩ về nó khi tôi viết bài đăng. Trên thực tế, điều này là đủ:print -l "${(fu)$(<infile)}"
Dimitre Radoulov

1
Thật tuyệt, cảm ơn vì đã cập nhật bài viết của bạn - Tôi cũng có quyền tự do sửa lỗi awkđầu ra mẫu.
mkuity0

9

Đưa chúng qua sortuniq. Điều này loại bỏ tất cả các bản sao.

uniq -dchỉ cung cấp các bản sao, chỉ cung cấp các bản sao uniq -uduy nhất (bản sao dải).


phải sắp xếp đầu tiên bằng vẻ bề ngoài của nó
brabster

1
Vâng, bạn làm. Hay chính xác hơn, bạn cần nhóm tất cả các dòng trùng lặp lại với nhau. Sắp xếp thực hiện điều này theo định nghĩa mặc dù;)
Matthew Scharley

Ngoài ra, uniq -uKHÔNG phải là hành vi mặc định (xem phần chỉnh sửa trong câu trả lời của tôi để biết chi tiết)
Matthew Scharley

7

Với AWK bạn có thể làm, tôi thấy nó nhanh hơn sắp xếp

 ./yourscript.ksh | awk '!a[$0]++'

Đó chắc chắn là cách yêu thích của tôi để thực hiện công việc, cảm ơn rất nhiều! Đặc biệt đối với các tệp lớn hơn, các giải pháp sắp xếp | uniq có thể không phải là điều bạn muốn.
Schmitzi

1

Duy nhất, theo yêu cầu, (nhưng không được sắp xếp);
sử dụng ít tài nguyên hệ thống hơn dưới ~ 70 phần tử (như đã được kiểm tra theo thời gian);
được viết để lấy đầu vào từ stdin,
(hoặc sửa đổi và đưa vào tập lệnh khác):
(Bash)

bag2set () {
    # Reduce a_bag to a_set.
    local -i i j n=${#a_bag[@]}
    for ((i=0; i < n; i++)); do
        if [[ -n ${a_bag[i]} ]]; then
            a_set[i]=${a_bag[i]}
            a_bag[i]=$'\0'
            for ((j=i+1; j < n; j++)); do
                [[ ${a_set[i]} == ${a_bag[j]} ]] && a_bag[j]=$'\0'
            done
        fi
    done
}
declare -a a_bag=() a_set=()
stdin="$(</dev/stdin)"
declare -i i=0
for e in $stdin; do
    a_bag[i]=$e
    i=$i+1
done
bag2set
echo "${a_set[@]}"

0

Tôi nhận được một mẹo tốt hơn để có được các mục không trùng lặp trong một tệp

awk '$0 != x ":FOO" && NR>1 {print x} {x=$0} END {print}' file_name | uniq -f1 -u
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.