Як покласти рядок пошуку з 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.

Спасибі заздалегідь.

Відповіді:


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 може шукати шаблони в декількох файлах, тому не потрібно використовувати оператор АБО / НЕ.

Чи є мінімальна різниця між "-wq" і "-qw" у будь-якій команді?
Mareyes

2
Ні, обидва вони надають параметри -q та -w для переключення.
glenn jackman

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 ++)). Забудь.
Лучано Андресс Мартіні

1
Слід зазначити , що n=$((n++))ніколи не буде змінювати значення п тому , що n++це повідомлення приріст: вона повертає поточне значення п, то прирощення п; то ви встановлюєте змінну на повернене значення, яке було початковим n.
glenn jackman

Якщо ви можете припустити, що ваші імена файлів не містять нових рядків local IFS=$'\n'; matches=( $(grep -lw "$users" "$file1" "$file2") )та використання case "${#matches[@]" in. @glenn: ця ідея стосується і вашої відповіді, щоб уникнути декількох викликів grep. Трубопровід до нього grep -l | wc -lтакож спрацює.
Пітер Кордес

@ Пітер, на який варто було б записати окрему відповідь ІМО.
Стівен Кітт

@StephenKitt: Я не збирався, тому що не міг знайти пуленепробивного способу обробки символів довільних файлів. Але оскільки ти вважаєш, що це того варте, я все-таки написав це.
Пітер Кордес

2

Якщо ваші імена файлів не містять нових рядків, ви можете уникнути декількох викликів 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, але я не впевнений, як аналізувати вихід . Bash var=( array elements )не має способу використовувати \0роздільник, а не \n. Може, mapfileвбудований Баш може це зробити? Але, мабуть, ні, тому що ви вказали роздільник з -d string.


Можна count=$(grep -l | wc -l), але тоді у вас є два зовнішні процеси, тож ви можете просто запустити grepдва файли окремо. (Різниця між grepпроти wcнакладних витрат запуску мала по порівнянні з вилковим + + Exec динамічний компонувальник матеріал , щоб запустити окремий процес взагалі).

Крім того, 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, на жаль. Так, це має сенс для друку розділених файлів так, \0як я вже згадував у відповіді, але як можна змусити башти, щоб це розібрати? Ви не можете встановити IFS=$'\0'або в основному робити що-небудь інше з find -print0виведенням стилів безпосередньо з bash.
Пітер Кордес

Так, я прочитав занадто швидко, пропустивши цей абзац, і не продумав (через що я видалив свій коментар). Можливо, є спосіб, але я не можу придумати його лише зараз, і як ви говорите, не варто робити рішення занадто складним, коли grepдля вирішення справи все-таки є лише кілька с.
Стівен Кітт

1
mapfile -d $'\0'сортування робіт, але він замінює нові рядки пробілами (я не пробував налаштування IFS).
Стівен Кітт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.