Як перевірити, чи існує рядок у файлі з Bash?


337

У мене є файл, який містить імена каталогів:

my_list.txt :

/tmp
/var/tmp

Я хотів би зареєструватися в Bash перед тим, як додати ім'я каталогу, якщо це ім’я вже існує у файлі.


Щоб знайти всі рядки у файлі, ви можете запустити grep у циклі FOR: unix.stackexchange.com/a/462445/43233
Noam Manos

Відповіді:


655
grep -Fxq "$FILENAME" my_list.txt

Статус виходу 0 (вірно), якщо ім'я знайдено, 1 (помилково), якщо ні, значить:

if grep -Fxq "$FILENAME" my_list.txt
then
    # code if found
else
    # code if not found
fi

Пояснення

Ось відповідні розділи чоловічої сторінки дляgrep :

grep [options] PATTERN [FILE...]

-F, --fixed-strings

        Інтерпретувати PATTERN як список фіксованих рядків, розділених новими рядками, будь-який з яких повинен відповідати.

-x, --line-regexp

        Виберіть лише ті відповідники, які точно відповідають цілому рядку.

-q, --quiet,--silent

        Тихо; не записуйте нічого на стандартний вихід. Вийдіть негайно із нульовим статусом, якщо знайдено відповідність, навіть якщо виявлена ​​помилка. Також дивіться опцію -sабо --no-messages.

Помилка обробки

Як справедливо зазначається в коментарях, вищевказаний підхід мовчки розглядає випадки помилок так, ніби знайдений рядок. Якщо ви хочете попрацювати з помилками по-іншому, вам доведеться опустити -qпараметр та виявити помилки на основі стану виходу:

Зазвичай статус виходу дорівнює 0, якщо вибрані рядки знайдені, а 1 - в іншому випадку. Але статус виходу 2 , якщо сталася помилка, якщо -qабо --quietабо --silentпараметр не використовується , і обрана рядок знайдена. Однак зауважте, що POSIX надає мандат лише для таких програм, як grep, cmpі diff, що статус виходу у разі помилки буде більше 1; Тому радимо для переносимості використовувати логіку, яка перевіряє цей загальний стан замість суворої рівності з 2.

Щоб придушити нормальний вихід з grep, ви можете перенаправити його на /dev/null. Зауважте, що стандартна помилка залишається непрямою, тому будь-які повідомлення про помилки, які grepможуть надрукуватись, потраплять на консоль, як ви, мабуть, хочете.

Для обробки трьох випадків ми можемо використовувати caseоператор:

case `grep -Fx "$FILENAME" "$LIST" >/dev/null; echo $?` in
  0)
    # code if found
    ;;
  1)
    # code if not found
    ;;
  *)
    # code if an error occurred
    ;;
esac

2
Якщо я виконую цю команду з bash script, як зловити 0 або 1 в змінну?
Торен

6
@Toren Доступ до останнього виходу можна отримати за допомогою $?. ви також можете використовувати команду grep поряд із ifтвердженням (як показано в оновленій відповіді).
Шон Чін

5
Ви можете використовувати, grep -Fqx "$FILENAME"і вам не доведеться турбуватися про символи регулярного виразів у змінній змісті, і вам не доведеться використовувати їх у пошуковій рядку.
Призупинено до подальшого повідомлення.

4
Кілька приміток для людей, які дивляться на цю відповідь: 1) У баші 0 - це завжди правда, а все інше - завжди помилково; 2) Використовуйте прапор -x лише якщо ви хочете, щоб весь рядок точно відповідав. Якщо ви просто хочете дізнатися, чи існує рядок у файлі взагалі, залиште це вимкнено. Якщо ви хочете встановити, чи точно існує ваш рядок, але не обов'язково відповідати цілому рядку (тобто, як ціле слово), використовуйте -w.
Шмік

1
Я не розумію, що -q / --silentце потрібно? Це помилково говорить "все добре", щоб ударити, навіть якщо помилка. Якщо я це зрозумів правильно. Здається, в цьому випадку недосконала концепція.
redanimalwar

90

Щодо наступного рішення:

grep -Fxq "$FILENAME" my_list.txt

Якщо вам цікаво (як і я), що -Fxqозначає простою англійською мовою:

  • F: Впливає на інтерпретацію PATTERN (фіксований рядок замість регулярного вираження)
  • x: Збігайте весь рядок
  • q: Shhhhh ... мінімальний друк

З файлу man:

-F, --fixed-strings
    Interpret  PATTERN  as  a  list of fixed strings, separated by newlines, any of which is to be matched.
    (-F is specified by POSIX.)
-x, --line-regexp
    Select only those matches that exactly match the whole line.  (-x is specified by POSIX.)
-q, --quiet, --silent
    Quiet; do not write anything to standard output.  Exit immediately with zero status  if  any  match  is
          found,  even  if  an error was detected.  Also see the -s or --no-messages option.  (-q is specified by
          POSIX.)

5
-F не ​​впливає на обробку файлів, це впливає на інтерпретацію PATTERN. Зазвичай PATTERN інтерпретується як регулярний вираз, але з -F він буде інтерпретуватися як фіксований рядок.
Адам S

41

Три мої думки:

1) Короткий тест на ім’я в контурі (я не впевнений, що це може бути у вашому випадку)

ls -a "path" | grep "name"


2) Короткий тест на рядок у файлі

grep -R "string" "filepath"


3) Більш довгий скрипт за допомогою регулярного вираження:

#!/bin/bash

declare file="content.txt"
declare regex="\s+string\s+"

declare file_content=$( cat "${file}" )
if [[ " $file_content " =~ $regex ]] # please note the space before and after the file content
    then
        echo "found"
    else
        echo "not found"
fi

exit

Це повинно бути швидше, якщо вам доведеться протестувати кілька рядків на вміст файлу, використовуючи цикл, наприклад, змінивши регулярний вираз на будь-якому файлі.


10
Чому потрібні пробіли до і після $ file_contenet?
EminezArtus

Плюс 1 за швидке рішення та більш узагальнений варіант
Вассадамо,

19

Простіший спосіб:

if grep "$filename" my_list.txt > /dev/null
then
   ... found
else
   ... not found
fi

Порада: надішліть, /dev/nullякщо ви хочете статус виходу команди, але не виходи.


1
або використовувати те -qсаме, що --quiet:)
rogerdpack

погодьтеся -qтакож на найкращу відповідь тут, і це четверте місце. немає справедливості в цьому світі.
тацу

14

Найпростішим і найпростішим способом було б:

isInFile=$(cat file.txt | grep -c "string")


if [ $isInFile -eq 0 ]; then
   #string not contained in file
else
   #string is in file at least once
fi

grep -c поверне кількість підрахунків, скільки разів в рядку виникає рядок.


5

Якщо я правильно зрозумів ваше запитання, це повинно робити все, що вам потрібно.

  1. ви можете вказати каталог, який ви хочете додати через змінну $ check
  2. якщо каталог вже в списку, вихід "dir вже вказаний"
  3. якщо каталог ще не в списку, він додається до my_list.txt

В одному рядку :check="/tmp/newdirectory"; [[ -n $(grep "^$check\$" my_list.txt) ]] && echo "dir already listed" || echo "$check" >> my_list.txt


Вам не потрібно перевіряти вихід grep, ви можете просто використовувати grep -qта дзвонити grep безпосередньо з так, ifяк це робить Томас у своїй відповіді. Крім того, питання не включало перевірку існування каталогу перед тим, як додати його до списку (це може бути список видалених каталогів, зрештою).
сорпігал

Я видалив приклад сценарію, він нічого не додав до відповіді, яку дав Томас.
lecodesportif

3

Якщо ви просто хочете перевірити наявність одного рядка, вам не потрібно створювати файл. Наприклад,

if grep -xq "LINE_TO_BE_MATCHED" FILE_TO_LOOK_IN ; then
  # code for if it exists
else
  # code for if it does not exist
fi  

3

Моя версія з використанням fgrep

  FOUND=`fgrep -c "FOUND" $VALIDATION_FILE`
  if [ $FOUND -eq 0 ]; then
    echo "Not able to find"
  else
    echo "able to find"     
  fi  

Я не бачу -cваріанту вfgrep --help
Нам G VU

3
grep -E "(string)" /path/to/file || echo "no match found"

-E варіант змушує grep використовувати регулярні вирази


1

Рішення @ Thomas чомусь не працювало для мене, але у мене була довша рядок зі спеціальними символами та пробілами, тому я просто змінив такі параметри:

if grep -Fxq 'string you want to find' "/path/to/file"; then
    echo "Found"
else
    echo "Not found"
fi

Сподіваюся, це комусь допоможе


0

Для мене працює безвідступне рішення:

MY_LIST=$( cat /path/to/my_list.txt )



if [[ "${MY_LIST}" == *"${NEW_DIRECTORY_NAME}"* ]]; then
  echo "It's there!"
else
echo "its not there"
fi

на основі: https://stackoverflow.com/a/229606/3306354


1
Використовує занадто багато пам'яті, якщо файл великий. grep -qописаний у прийнятій відповіді є найбільш ефективним підходом.
codeforester

0
grep -Fxq "String to be found" | ls -a
  • grep will допоможе вам перевірити вміст
  • ls перелічить усі файли

@ThomWiggers Я спробував те саме, і це працювало на мене.
Шіной Шаджі

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.