Как проверить, существует ли строка в файле с 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 (true), если имя найдено, 1 (false), если нет, поэтому:

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, как перехватить 0 или 1 в переменную?
Торен

6
@Toren К последнему состоянию выхода можно получить доступ с помощью $?. Вы также можете использовать команду grep вместе с ifоператором (как показано в обновленном ответе).
Шон Чин

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

4
Несколько замечаний для людей, которые смотрят на этот ответ: 1) В bash 0 всегда истинно, а все остальное всегда ложно 2) Используйте флаг -x, только если вы хотите, чтобы вся строка точно совпадала. Если вы просто хотите узнать, существует ли вообще ваша строка в файле, не указывайте это. Если вы хотите узнать, существует ли ваша строка точно, но не обязательно соответствует всей строке (т. Е. Как целое слово), используйте -w.
Шмик,

1
Я не понимаю, -q / --silentэто нужно? Это ложно говорит "все хорошо", чтобы bash, даже если происходит ошибка. Если я правильно понял. Кажется некорректной концепцией для этого случая.
redanimalwar

90

Относительно следующего решения:

grep -Fxq "$FILENAME" my_list.txt

Если вам интересно (как я это сделал), что -Fxqозначает на простом английском:

  • F: Влияет на интерпретацию PATTERN (фиксированная строка вместо регулярного выражения)
  • x: Соответствовать всей линии
  • q: Тссс ... минимальная печать

Из файла 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 он будет интерпретироваться как фиксированная строка.
Адам С

41

Три метода в моей голове:

1) Короткий тест для имени в пути (я не уверен, что это может быть ваш случай)

ls -a "path" | grep "name"


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

grep -R "string" "filepath"


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

#!/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также лучший ответ здесь, и это четвертое место. нет справедливости в этом мире.
Tatsu

15

Самый простой и простой способ будет:

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

Решение без grep, работает для меня:

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.