Làm thế nào để đặt một tìm kiếm chuỗi với lệnh grep vào câu lệnh if?


11

Tôi muốn tìm kiếm nhiều chuỗi trong hai tập tin. Nếu một chuỗi được tìm thấy trong cả hai tệp, sau đó tạo một cái gì đó. Nếu một chuỗi được tìm thấy chỉ trong một tệp, sau đó tạo một thứ khác.

Các lệnh của tôi là tiếp theo:

####This is for the affirmative sentence in both files
if grep -qw "$users" "$file1" && grep -qw "$users" "$file2"; then

####This is for the affirmative sentence in only one file, and negative for the other one
if grep -qw "$users" "$file1" ! grep -qw "$users" "$file2"; then

nó có phải là một cách chính xác để từ chối và khẳng định các tuyên bố? pd Tôi đang sử dụng vỏ KSH.

Cảm ơn bạn trước.

Câu trả lời:


11

Thử đi:

if grep -wq -- "$user" "$file1" && grep -wq -- "$user" "$file2" ; then
   echo "string avail in both files"
elif grep -wq -- "$user" "$file1" "$file2"; then
   echo "string avail in only one file"
fi
  • grep có thể tìm kiếm các mẫu trong nhiều tệp, do đó không cần sử dụng toán tử OR / NOT.

Có sự khác biệt tối thiểu giữa "-wq" và "-qw" trong mỗi lệnh không?
Mareyes

2
Không, cả hai đều cung cấp tùy chọn -q và -w cho grep.
glenn jackman

3
Tại sao không có trích dẫn về việc mở rộng biến?
D. Ben Knoble

@ D.BenKnoble có chính xác không: "$ user" "$ file1"?
Mareyes

1
@Mareyes đúng vậy
D. Ben Knoble

13

Một lựa chọn khác:

grep -qw -- "$users" "$file1"; in_file1=$?
grep -qw -- "$users" "$file2"; in_file2=$?

case "${in_file1},${in_file2}" in
    0,0) echo found in both files ;;
    0,*) echo only in file1 ;;
    *,0) echo only in file2 ;;
      *) echo in neither file ;;
esac

Hey cảm ơn bạn cho cách tùy chọn. Nó hoạt động đủ tốt !! hoạt động hoàn hảo trong một kịch bản khác mà tôi có.
Mareyes

Đó là một ý tưởng tuyệt vời! Luôn luôn học hỏi.
Joe

7
n=0

#Or if you have more files to check, you can put your while here. 
grep -qw -- "$users" "$file1" && ((n++))
grep -qw -- "$users" "$file2" && ((n++))

case $n in 
   1) 
       echo "Only one file with the string"
    ;;
   2)
       echo "The two files are with the string"
   ;;
   0)
       echo "No one file with the string"
   ;;
   *)
       echo "Strange..."
   ;;
esac 

Lưu ý: ((n++))là phần mở rộng ksh (cũng được hỗ trợ bởi zshbash). Trong shcú pháp POSIX , bạn cần n=$((n + 1))thay thế.


Ôi xin lỗi tôi đã gõ $ ((n ++)) thay vì ((n ++)). Quên.
Luciano Andress Martini

1
Lưu ý rằng n=$((n++))KHÔNG BAO GIỜ thay đổi giá trị của n vì n++bài tăng: nó trả về giá trị hiện tại của n, sau đó tăng n; sau đó bạn đặt biến thành giá trị trả về là giá trị n ban đầu.
glenn jackman

Nếu bạn có thể giả sử tên tệp của mình không chứa dòng mới local IFS=$'\n'; matches=( $(grep -lw "$users" "$file1" "$file2") )và sử dụng case "${#matches[@]" in. @glenn: ý tưởng này cũng áp dụng cho câu trả lời của bạn, để tránh nhiều lời mời grep. Đường ống để nó grep -l | wc -lcũng sẽ làm việc.
Peter Cordes

@Peter đáng để viết như một câu trả lời riêng IMO.
Stephen Kitt

@StephenKitt: Tôi sẽ không làm thế bởi vì tôi không thể tìm ra cách chống đạn để xử lý các ký tự tên tệp tùy ý. Nhưng vì bạn nghĩ nó đáng giá, dù sao tôi cũng đã viết nó lên.
Peter Cordes

2

Nếu tên tệp của bạn không chứa dòng mới, bạn có thể tránh được nhiều yêu cầu grepbằng cách grep in tên của các tệp phù hợp và đếm kết quả.

 local IFS=$'\n'    # inside a function.  Otherwise use some other way to save/restore IFS
 matches=( $(grep -lw "$users" "$file1" "$file2") )

Số lượng các trận đấu là "${#matches[@]}".

Có thể có một cách để sử dụng grep --null -lwở đây, nhưng tôi không chắc cách phân tích đầu ra . Bash var=( array elements )không có cách sử dụng một \0dấu phân cách thay vì \n. Có lẽ mapfilenội dung của bash có thể làm điều đó? Nhưng có lẽ là không, bởi vì bạn chỉ định dấu phân cách với -d string.


Bạn có thể count=$(grep -l | wc -l), nhưng sau đó bạn có hai quy trình bên ngoài để bạn có thể chỉ chạy riêng greptrên hai tệp. (Sự khác biệt giữa chi phí khởi động grepso với wcchi phí khởi động là nhỏ so với fork + exec + công cụ liên kết động để bắt đầu một quy trình riêng biệt).

Ngoài ra, với wc -lbạn không tìm hiểu tập tin phù hợp.


Với kết quả thu được trong một mảng, đó có thể là những gì bạn muốn hoặc nếu có chính xác 1 kết quả khớp, bạn có thể kiểm tra xem đó có phải là đầu vào đầu tiên hay không.

local IFS=$'\n'    # inside a function.  Otherwise use some other way to save/restore IFS
matches=( $(grep -lw "$users" "$file1" "$file2") )

# print the matching filenames
[[ -n $matches ]] && printf  'match in %s\n'  "${matches[@]}"

# figure out which input position the name came from, if there's exactly 1.
if [[ "${#matches[@]" -eq 1 ]]; then
    if [[ $matches == "$file1" ]];then
        echo "match in file1"
    else
        echo "match in file2"
    fi
fi

$matcheslà tốc ký cho ${matches[0]}, phần tử mảng đầu tiên.


@StephenKitt: Ý tôi là -zkhông phải -0vậy. Vâng, điều đó sẽ khiến grep in các tên tệp được phân tách bằng \0như tôi đã đề cập trong câu trả lời, nhưng làm thế nào bạn có thể nhận bash để phân tích điều đó? Bạn không thể thiết lập IFS=$'\0'hoặc về cơ bản làm bất cứ điều gì khác với find -print0đầu ra kiểu trực tiếp bằng bash.
Peter Cordes

Có, tôi đã đọc quá nhanh, thiếu đoạn đó và không nghĩ gì cả (đó là lý do tại sao tôi xóa bình luận của mình). Cũng có thể có một cách, nhưng tôi không thể nghĩ ra ngay bây giờ, và như bạn nói không đáng để làm cho giải pháp trở nên quá phức tạp khi chỉ có một vài grepgiây để giải quyết.
Stephen Kitt

1
mapfile -d $'\0'sắp xếp các tác phẩm, nhưng nó thay thế các dòng mới bằng khoảng trắng (Tôi chưa thử điều chỉnh IFS).
Stephen Kitt
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.