find e sed (localizar e excluir)

0

Eu tenho uma pasta com poucas subpastas e arquivos dentro dela. Além disso, há um arquivo CSV contendo os nomes das subpastas e padrões existentes nos arquivos dentro da subpasta.

O que eu quero fazer é ler o arquivo CSV usando o loop while dentro da pasta principal e excluir os arquivos de padrões correspondentes usando sed . Estou usando este script shell bash no Unix:

IFS=","
while read f1 f2
do
 find $f2/ -name vcs*.pv -exec sed -i '/$f1/d' {} +
done > export.csv

Erro:

find: 'ap01\r/': No such file or directory.

Arquivo CSV:

S2AEC67X1,ap01

Ele está lendo o valor f2 corretamente, mas não está fazendo o resto. Estou mantendo o arquivo CSV dentro do diretório principal que contém todas as subpastas.

    
por sharmili mukherjee 20.01.2018 / 16:06

2 respostas

1

Isto: ap01\r

Indica que há um retorno de carro após a string api01 . Tente remover isso do arquivo CSV.

Atualização: leia também o comentário de @RomanPerekhrest. Você vai querer mudar o > para um < no seu while loop (se de fato você deve usar um while loop, mas isso é outra discussão completamente!).

    
por 20.01.2018 / 16:16
1

Existem alguns problemas com o código e o arquivo de entrada nesta pergunta:

  1. O arquivo de entrada obviamente tem retornos de carro à direita ( \r ) em cada linha. Provavelmente, isso deve ter sido criado em uma máquina Windows como um arquivo de texto do DOS. A maneira usual de se livrar desses retornos de carro é executar dos2unix no arquivo. Veja, por exemplo, a pergunta What é '^ M' e como posso me livrar disso?

  2. Todas as expansões variáveis devem ser duplamente citadas. Em seu comando, você usa $f2 sem aspas como um nome de caminho para um diretório. Isso falharia se $f2 contivesse espaços.

  3. As aspas simples impedem que o shell expanda uma variável, o que significa que o script sed está procurando por linhas que correspondam à expressão regular literal $f1 . Essa expressão regular nunca corresponderá, pois o $ corresponderá apenas ao final de uma linha e não haverá linha que termine e, em seguida, contenha os caracteres f1 na mesma linha . Duplicar o script de edição sed fará com que o shell expanda a variável $f1 antes de invocar sed .

  4. O padrão vcs*.pv deve ser um argumento para a opção -name de find , mas como não é citada, ele se expande para qualquer nome no diretório atual que corresponda a esse padrão de globbing. Portanto, se você tivesse um arquivo no diretório atual cujo nome fosse vcs-test.pv , find seria invocado com -name vcs-test.pv e você só encontraria arquivos com esse nome. Se você tivesse vários nomes correspondentes no diretório atual, você faria com que find reclamasse sobre opções desconhecidas.

  5. O arquivo export.csv é enviado para (e esvaziado antes que qualquer saída do loop aconteça). Você gostaria que o loop fosse lido a partir dele. Isso envolve alterar > para < .

O script corrigido:

while IFS=',' read f1 f2; do
    find "$f2" -type f -name 'vcs*.pv' -exec sed -i "/$f1/d" {} +
done <export.csv

Também adicionei -type f à linha de comando find , pois provavelmente não queremos que os nomes de diretório sejam pegos acidentalmente. Eu também fiz isso para que a variável IFS seja definida somente para o comando read .

Esta é uma variação do acima, no caso de todos os arquivos estarem diretamente abaixo do diretório principal cujo nome você está lendo no arquivo CSV:

while IFS=',' read dir pattern; do
    for name in "$dir"/vcs*.pv; do
        test -f "$name" && sed -i "s/$pattern/d" "$name"
    done
done <export.csv

O bom disso é que você se livra de find . O ruim é que agora você tem uma invocação de sed por arquivo (geralmente não é um problema, a menos que você tenha centenas ou mais arquivos).

A seguir, uma variação acima, que exclui linhas dependendo da string lida no arquivo CSV. A diferença é que os trechos de código, acima de tudo, interpretam o padrão como uma expressão regular , não como uma string fixa. Isso é importante se a string contiver caracteres interpretados como "especiais" em uma expressão regular, como . , * , [ , ] etc.

while IFS=',' read dir string; do
    for name in "$dir"/vcs*.pv; do
        [ ! -f "$name" ] && continue
        grep -v -F -e "$string" "$name" >"$name.tmp" && mv -f "$name.tmp" "$name"
    done
done <export.csv
    
por 04.02.2018 / 08:59