Como testar se a string existe no arquivo com o Bash?


337

Eu tenho um arquivo que contém nomes de diretório:

my_list.txt :

/tmp
/var/tmp

Gostaria de fazer check-in no Bash antes de adicionar um nome de diretório, se esse nome já existir no arquivo.


Para encontrar todas as cordas dentro de um arquivo, você pode executar grep em loop FOR: unix.stackexchange.com/a/462445/43233
Noam Manos

Respostas:


654
grep -Fxq "$FILENAME" my_list.txt

O status de saída é 0 (verdadeiro) se o nome foi encontrado, 1 (falso) se não, então:

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

Explicação

Aqui estão as seções relevantes da página de manual paragrep :

grep [options] PATTERN [FILE...]

-F, --fixed-strings

        Interprete PATTERN como uma lista de cadeias fixas, separadas por novas linhas, qualquer uma das quais deve ser correspondida.

-x, --line-regexp

        Selecione apenas as correspondências que correspondam exatamente à linha inteira.

-q, --quiet,--silent

        Quieto; não escreva nada na saída padrão. Saia imediatamente com o status zero se alguma correspondência for encontrada, mesmo que um erro tenha sido detectado. Veja também a opção -sou --no-messages.

Manipulação de erros

Como corretamente apontado nos comentários, a abordagem acima trata silenciosamente os casos de erro como se a sequência fosse encontrada. Se você deseja lidar com erros de uma maneira diferente, precisará omitir a -qopção e detectar erros com base no status de saída:

Normalmente, o status de saída é 0 se as linhas selecionadas forem encontradas e 1 caso contrário. Mas o status de saída é 2 se ocorrer um erro, a menos que a opção -qou --quietou --silentseja usada e uma linha selecionada seja encontrada. Note, no entanto, que POSIX apenas mandatos, para programas como grep, cmpe diffque o status de saída em caso de erro ser maior do que 1; portanto, é recomendável, por uma questão de portabilidade, usar a lógica que testa essa condição geral em vez da estrita igualdade com 2.

Para suprimir a saída normal de grep, você pode redirecioná-la para /dev/null. Observe que o erro padrão permanece sem direcionamento; portanto, qualquer mensagem de erro que greppossa ser impressa será finalizada no console como você provavelmente desejaria.

Para lidar com os três casos, podemos usar uma casedeclaração:

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
Se eu executar este comando a partir do script bash, como capturar 0 ou 1 em uma variável?
Toren

6
@Toren O status de saída mais recente pode ser acessado usando $?. você também pode usar o comando grep ao lado da ifinstrução (como mostrado na resposta atualizada).
Shawn Chin

5
Você pode usar grep -Fqx "$FILENAME"e não precisa se preocupar com caracteres regex no conteúdo da variável e não precisará usá-los na string de pesquisa.
Pausado até novo aviso.

4
Algumas notas para as pessoas que olham para esta resposta: 1) No bash, 0 é sempre verdadeiro e tudo o mais é sempre falso 2) Use apenas o sinalizador -x se desejar que toda a linha corresponda exatamente. Se você apenas deseja descobrir se sua string existe no arquivo, deixe-a assim. Se você quiser descobrir se sua string existe exatamente, mas sem corresponder necessariamente a uma linha inteira (por exemplo, como uma palavra inteira), use -w.
217 Schmick

11
Eu não entendo o que -q / --silenté necessário? Diz falsamente "tudo de bom" para bater, mesmo que ocorra um erro. Se eu entendi direito. Parece um conceito defeituoso para este caso.
Redanimalwar # 7/19

90

Em relação à seguinte solução:

grep -Fxq "$FILENAME" my_list.txt

Caso você esteja se perguntando (como eu) o que -Fxqsignifica em inglês simples:

  • F: Afeta como PATTERN é interpretado (sequência fixa em vez de uma expressão regular)
  • x: Corresponder a linha inteira
  • q: Shhhhh ... impressão mínima

No arquivo 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 não afeta o processamento do arquivo, afeta como o PATTERN é interpretado. Normalmente, PATTERN é interpretado como uma expressão regular, mas com -F será interpretado como uma sequência fixa.
12133 Adam S

41

Três métodos em minha mente:

1) Teste curto para um nome em um caminho (não tenho certeza se esse pode ser o seu caso)

ls -a "path" | grep "name"


2) Teste curto para uma string em um arquivo

grep -R "string" "filepath"


3) Script bash mais longo usando regex:

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

Isso deve ser mais rápido se você precisar testar várias strings em um conteúdo de arquivo usando um loop, por exemplo, alterando a regex em qualquer ciclo.


10
Por que os espaços são necessários antes e depois do $ file_contenet?
EminezArtus 03/02

Mais 1 para a solução rápida e mais generalizada #
Wassadamo 26/07/19

19

Maneira mais simples:

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

Dica: envie para /dev/nullse desejar o status de saída do comando, mas não as saídas.


11
ou o uso -qque é o mesmo que --quiet:)
rogerdpack

concordar com a -qmelhor resposta também aqui e é o quarto lugar. não há justiça neste mundo.
Tatsu # 15/19

14

A maneira mais fácil e simples seria:

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 retornará a contagem de quantas vezes a string ocorre no arquivo.


5

Se entendi sua pergunta corretamente, isso deve fazer o que você precisa.

  1. você pode especificar o diretório que você gostaria de adicionar através da variável $ check
  2. se o diretório já estiver na lista, a saída será "dir já listado"
  3. se o diretório ainda não estiver na lista, ele será anexado a my_list.txt

Em uma linha :check="/tmp/newdirectory"; [[ -n $(grep "^$check\$" my_list.txt) ]] && echo "dir already listed" || echo "$check" >> my_list.txt


Você não precisa testar a saída do grep, basta usar grep -qe chamar o grep diretamente, ifcomo Thomas faz em sua resposta. Além disso, a pergunta não incluiu a verificação da existência do diretório antes de adicioná-lo à lista (afinal, poderia ser uma lista de diretórios excluídos).
sorpigal

Eu removi o script de exemplo, ele não adicionou nada à resposta dada por Thomas.
Lecodesportif

3

Se você apenas deseja verificar a existência de uma linha, não precisa criar um arquivo. Por exemplo,

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

Minha versão usando fgrep

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

Não vejo a -copção emfgrep --help
Nam G VU

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

A opção -E faz com que o grep use expressões regulares


1

A solução do @ Thomas não funcionou para mim por algum motivo, mas eu tinha uma string mais longa com caracteres especiais e espaços em branco, então apenas alterei os parâmetros assim:

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

Espero que ajude alguém




-1
if grep -q "$Filename$" my_list.txt
   then
     echo "exist"
else 
     echo "not exist"
fi
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.