วิธีที่จะทำให้การค้นหาสตริงด้วยคำสั่ง grep เป็นคำสั่ง if?


11

ฉันต้องการค้นหาหลายสายในสองไฟล์ หากพบหนึ่งสตริงในไฟล์ทั้งสองให้สร้างบางอย่าง หากพบหนึ่งสตริงในไฟล์เดียวให้สร้างอีกอย่าง

คำสั่งของฉันคือต่อไป:

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

มันเป็นวิธีที่ถูกต้องในการปฏิเสธและยืนยันข้อความ? pd ฉันใช้ KSH shell

ขอบคุณล่วงหน้า.

คำตอบ:


11

ลองสิ่งนี้:

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 สามารถค้นหารูปแบบในหลายไฟล์ได้ดังนั้นไม่จำเป็นต้องใช้ตัวดำเนินการ OR / NOT

มีความแตกต่างน้อยที่สุดระหว่าง "-wq" และ "-qw" ในแต่ละคำสั่งหรือไม่?
Mareyes

2
ไม่ทั้งคู่จัดเตรียมอ็อพชัน -q และ -w เพื่อ grep
เกล็นแจ็

3
ทำไมไม่มีเครื่องหมายคำพูดในการขยายตัวของตัวแปร
D. Ben Knoble

@ D.BenKnoble ถูกต้องหรือไม่: "$ user" "$ file1"?
Mareyes

1
@ Mareyes ใช่ถูกต้อง
D. Ben Knoble

13

ตัวเลือกอื่น:

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

เฮ้ขอบคุณสำหรับวิธีที่ไม่จำเป็น มันทำงานได้ดีพอ !! ทำงานได้อย่างสมบูรณ์แบบในสคริปต์อื่นที่ฉันมี
Mareyes

เป็นความคิดที่ดีมาก! เรียนรู้อยู่เสมอ
โจ

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 

หมายเหตุ: ((n++))เป็นส่วนขยาย ksh (รองรับโดยzshและbash) ในshไวยากรณ์POSIX คุณต้องการn=$((n + 1))แทน


โอ้ขอโทษฉันพิมพ์ $ ((n ++)) แทน ((n ++)) ลืม.
Luciano Andress Martini

1
โปรดทราบว่าn=$((n++))จะไม่เปลี่ยนค่าของ n เนื่องจากn++เป็นการเพิ่มขึ้นภายหลัง : ส่งคืนค่าปัจจุบันของ n จากนั้นเพิ่ม n จากนั้นคุณตั้งค่าตัวแปรเป็นค่าที่ส่งคืนซึ่งเป็นค่าเดิม
เกล็นแจ็

หากคุณสามารถสันนิษฐานได้ว่าชื่อไฟล์ของคุณไม่ได้มีการขึ้นบรรทัดใหม่, และการใช้งานlocal IFS=$'\n'; matches=( $(grep -lw "$users" "$file1" "$file2") ) case "${#matches[@]" in@glenn: ความคิดนี้มีผลกับคำตอบของคุณเช่นกันเพื่อหลีกเลี่ยงการร้องขอหลายgrepครั้ง ท่อไปgrep -l | wc -lยังจะทำงาน
Peter Cordes

@ ปีเตอร์ที่น่าจะเขียนขึ้นเป็นคำตอบที่แยก IMO
Stephen Kitt

@StephenKitt: ฉันไม่ได้ไปเพราะฉันไม่สามารถหาวิธีที่จะกันกระสุนในการจัดการตัวละครชื่อไฟล์โดยพลการ แต่เนื่องจากคุณคิดว่ามันคุ้มค่าฉันจึงเขียนมันต่อไป
Peter Cordes

2

หากชื่อไฟล์ของคุณไม่มีบรรทัดใหม่คุณสามารถหลีกเลี่ยงการเรียกใช้หลายรายการgrepโดยให้ grep พิมพ์ชื่อของไฟล์ที่ตรงกันและนับผลลัพธ์

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

"${#matches[@]}"จำนวนของการแข่งขันคือ

อาจมีวิธีใช้grep --null -lwที่นี่ แต่ฉันไม่แน่ใจว่าจะแยกวิเคราะห์ผลลัพธ์ได้อย่างไร ทุบตีvar=( array elements )ไม่ได้มีวิธีการใช้ที่คั่นแทน\0 \nบางทีmapfilebuiltin ของ bash สามารถทำได้หรือไม่ -d stringแต่อาจจะไม่ได้เพราะคุณระบุตัวคั่นด้วย


คุณสามารถทำได้count=$(grep -l | wc -l)แต่จากนั้นคุณมีกระบวนการภายนอกสองกระบวนการดังนั้นคุณอาจทำงานgrepบนสองไฟล์แยกกัน (ความแตกต่างระหว่างgrepกับwcค่าใช้จ่ายในการเริ่มต้นมีขนาดเล็กเมื่อเทียบกับ fork + exec + dynamic linker stuff เพื่อเริ่มกระบวนการแยกต่างหากเลย)

นอกจากนี้ยังมีwc -lคุณไม่พบออกซึ่งไฟล์จับคู่


ด้วยผลลัพธ์ที่ได้จากอาเรย์นั้นอาจเป็นสิ่งที่คุณต้องการอยู่แล้วหรือหากมีการจับคู่ที่ตรงกันทั้งหมด 1 รายการคุณสามารถตรวจสอบว่าเป็นอินพุทแรกหรือไม่

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

$matchesเป็นที่จดชวเลขสำหรับ${matches[0]}องค์ประกอบอาร์เรย์แรก


@StephenKitt: ฉันหมายถึง-zไม่ใช่-0อุ๊ปส์ ใช่ว่าจะทำให้ grep พิมพ์ชื่อไฟล์โดยแยกจากที่\0ฉันได้กล่าวไปแล้วในคำตอบ แต่คุณจะทุบตีเพื่อแยกคำตอบนั้นได้อย่างไร คุณไม่สามารถตั้งค่าได้IFS=$'\0'หรือโดยทั่วไปแล้วจะทำสิ่งใดกับfind -print0สไตล์เอาต์พุตโดยตรงโดยใช้ bash
Peter Cordes

ใช่ฉันอ่านเร็วเกินไปหายไปในย่อหน้านั้นและไม่ได้คิดสิ่งต่าง ๆ ผ่าน (ซึ่งเป็นเหตุผลที่ฉันลบความคิดเห็นของฉัน) อาจมีวิธี แต่ฉันไม่สามารถนึกได้ตอนนี้และอย่างที่คุณบอกว่ามันไม่คุ้มค่าที่จะทำวิธีแก้ปัญหาที่ซับซ้อนเกินไปเมื่อมีเพียงไม่กี่grepที่จะจัดการกับต่อไป
Stephen Kitt

1
mapfile -d $'\0'เรียงลำดับของงาน แต่แทนที่บรรทัดใหม่ด้วยช่องว่าง (ฉันไม่ได้ลอง tweaking IFS)
Stephen Kitt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.