Usando o comando grep com um conjunto de palavras
Eu tenho um arquivo de texto com algumas linhas e salvei o arquivo de texto em uma matriz usando
readarray -t array < Textfile
O arquivo de texto contém, por exemplo, o seguinte:
123
456
789
Agora eu gostaria de usar o comando grep para procurar os resultados em outro arquivo de texto usando o array, então imprima as linhas onde "123", "456" ou "789" aparecem. Para testar o array, tentei fazer o grep procurar por "123", "456" e "789" o mesmo arquivo de texto e gerar as linhas correspondentes, se pelo menos uma das três séries de caracteres aparecer.
eu tentei
grep "${array[*]}" Textfile
mas isso não mostra nenhum resultado. O que estou fazendo errado?
Respostas
Em primeiro lugar, você pode usar o próprio arquivo. Isso é muito mais simples do que tentar usar uma matriz de shell:
grep -f file1.txt file2.txt
Isso imprimirá quaisquer linhas file2.txtque correspondam a quaisquer linhas em file1.txt.
Se você tiver que usar um array por algum motivo, as coisas são mais complicadas. Você não pode simplesmente fazer grep "${array[*]}" Textfileporque "${array[*]}"irá expandir para a lista de elementos na matriz separados por um espaço:
$ array=("foo" "bar" "baz") $ echo "${array[*]}"
foo bar baz
O que significa que seu grepcomando se tornaria:
grep 'foo bar baz' file
E isso significa "grep for foo bar bazin the file file". O que você quer fazer é usar grep para foo, ou bar , ou baz . Isso pode ser feito usando a opção -E grepe juntando os padrões que você deseja pesquisar com |:
grep -E 'foo|bar|baz' file
Para fazer isso, você precisará fazer algo complicado como:
grep -E "$(printf '%s|' "${array[@]}" | sed 's/|$//')" file
Ou talvez:
grep -E "$(export IFS="|"; echo "${array[*]}")" file
No geral, é simplesmente melhor, mais rápido e fácil usar o arquivo e esquecer o array.
grep "${array[*]}" Textfile
Funcionará contanto que você defina IFScomo nova linha (ou qualquer coisa que comece com nova linha) e use --ou -epara garantir que ainda funcione mesmo se o primeiro elemento começar com -.
"${array[*]}"em shells semelhantes ao Korn, como "$*"em shells POSIX, expande-se para a lista de elementos unidos com o primeiro caractere de $IFS. O valor padrão de $IFSé <SPC><TAB><NL>( <SPC><TAB><NL><NUL>in zsh), portanto, por padrão, você obteria o elemento unido com caracteres SPC. Pois grep, você precisa que as diferentes expressões regulares sejam separadas por nova linha para grepfazer um loop para cada uma delas.
IFS=$'\n'
grep -e "${array[*]}" file
Em zsh, fazer grep -e "${(pj:\n:)array}" file(explicitamente joining com nova linha em vez de modificar $IFSglobalmente), ou grep -e$^array file(que expande o array no estilo fish/ rc, onde isso se torna grep -efirst -esecond fileoutra forma de fornecer vários padrões grep) seria mais limpo.
Outra opção é fazer:
printf '%s\n' "${array[@]}" | grep -f - file
Desta vez, passando a lista de padrões delimitada por nova linha sobre grepo stdin de, em vez de por meio de um argumento.
Essas abordagens funcionariam se seus padrões fossem strings fixas (com -F), expressões regulares estendidas (com -E) ou expressões regulares básicas (padrão).
Você pode querer ter certeza de que a lista de padrões não está vazia:
(( ${#array[@]} > 0 )) && grep ...
Chamar grepcom um padrão vazio produz resultados diferentes dependendo da grepimplementação e geralmente não do que você deseja.