Escreva um script de shell que moverá todos os arquivos no diretório atual contendo a palavra “hello” para uma pasta separada chamada “hello-world”

1

Escreva um script de shell que moverá todos os arquivos no diretório atual contendo a palavra hello para uma pasta separada chamada hello-world .

O diretório atual contém os arquivos a b c d e e f , dos quais os arquivos a , c e f contêm a palavra hello .

Como posso pesquisar a palavra hello nos arquivos do diretório atual e mover os arquivos que satisfazem a condição para outro diretório hello-world ?

    
por carlox warrior 30.01.2018 / 20:35

3 respostas

3

Vamos dividir isso em tarefas menores. Precisamos

  • crie um diretório hello-world , se ele não existir
  • identifique de forma confiável os arquivos que contêm o texto hello
  • mova de forma confiável os arquivos para o novo diretório.

Fazer um diretório é feito com mkdir . Ele sempre se recusará a criar um diretório que exista (recusando-se a sobrescrever tais diretórios), mas ele também possui um sinalizador -p , que, como mencionado em man mkdir

-p, --parents
       no error if existing, make parent directories as needed

não se preocupa em informar se o diretório que você está tentando criar já existe.

Assim, o primeiro comando do nosso script pode ser (verifique se você está no diretório correto primeiro).

mkdir -p hello-world

O segundo passo é identificar os arquivos que queremos. O comando mais óbvio para procurar texto nos arquivos é grep . Se quiséssemos apenas os nomes dos arquivos que contêm hello , podemos usar o -l flag, que, de acordo com man grep

-l, --files-with-matches
       Suppress  normal output; instead print the name of each input file
       from which output would normally have been printed.  The scanning
       will stop on the first match.

suprime a saída normal. Mas, embora possamos nos safar para o seu exemplo, nós realmente não queremos os nomes dos arquivos como saída, porque a saída de um comando é apenas texto, e nomes de arquivos podem conter todos os tipos de caracteres (do espaço humilde ao exótico). newline) que fará com que o shell veja algo diferente do nome real do arquivo para o qual você deseja fazer algo e se comporte de uma maneira que você não queria. Por exemplo, tendo configurado um diretório de teste para o seu script, se eu adicionar um arquivo que tenha um espaço em seu nome, veja o que acontece:

$ echo hello > with\ space
$ ls
a  b  c  d  e  f  with space
$ for i in $(grep -l hello *); do echo "$i"; done
a
c
f
with
space

O arquivo with space é tratado como dois arquivos separados.

Para identificar arquivos para que o shell faça alguma coisa com eles, devemos evitar a análise de nomes de arquivos. Poderíamos testar cada arquivo, ver se ele contém o texto e, se houver, movê-lo. Para fazer o loop de arquivos, como mostrado acima, podemos usar for , que tem uma sintaxe como esta:

for var in things; do stuff $var; done

Dentro de um loop for (e em qualquer outro lugar também), podemos usar o comando test , que às vezes se parece com [ e também é igual a [[ para fazer declarações condicionais, e se você quisesse verificar Se o arquivo estava vazio ou não, ou era de um determinado tipo, eu aconselho você a usar o comando test .

Mas você quer encontrar algum texto em particular, então devemos chamar grep , mas nós só queremos saber se procurando hello no arquivo foi bem-sucedido, então sabemos que temos que fazer algo com o arquivo.

Para fazer uma declaração condicional com base no sucesso de um comando, podemos usar if . if é frequentemente usado com test / [ / [[ , mas você não precisa disso, porque grep tem outro sinalizador útil:

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

Zero em Bash significa sucesso. Então, para fazer o que queremos, poderíamos escrever algo como

if grep -q hello file; then mv file hello-world; fi

(o fi é parte da sintaxe do comando if . Ele informa ao shell que você terminou seu if )

Eu normalmente escrevo scripts de shell em um shell interativo e apenas os scriptizo em um arquivo mais tarde, se de alguma forma, porque eu sou preguiçoso e principalmente escrevo scripts muito triviais. De qualquer forma, você pode escrever um script curto como um comando de uma linha separando os comandos com ; . Se algo der errado, pressione a tecla de seta para cima para editar o último comando ...

Não queremos executar esse comando uma vez para cada arquivo. Isso frustraria o objetivo de escrever um script para fazer o trabalho e nos poupar da digitação, então, para fazer um loop pelos arquivos, podemos usar for . Para evitar um erro de grep sobre o diretório hello-world , podemos adicionar outro sinalizador para excluí-lo.

Aqui está o meu comando de teste para fazer este trabalho e a saída que recebo dele no meu ambiente de teste:

$ mkdir -p hello-world; for file in *; do if grep -q --exclude-dir=hello-world -- hello "$file"; then echo mv -v -- "$file" hello-world; fi; done
mv -v -- a hello-world
mv -v -- c hello-world
mv -v -- f hello-world
mv -v -- with space hello-world

Este script não move nenhum arquivo, por causa de echo antes de mv , que mostra qual comando será executado a cada iteração do loop. Remova echo depois de testar se os comandos parecem corretos (note que nem sempre é possível usar echo para testes e talvez seja necessário introduzir algumas citações temporárias para fazer isso, mas funciona bem aqui).

* é todos os arquivos não ocultos no diretório atual. É mais seguro usar ./* , o que significa a mesma coisa, mas assegura que todos os caminhos iniciem com ./ (que é o endereço do diretório de trabalho atual . ), impedindo que nomes de arquivos começando com - sejam interpretados como opções. Nós lidamos com essa possibilidade adicionando -- a grep e ao comando mv para indicar o fim das opções. -v é o sinalizador detalhado de mv .

Devemos citar variáveis para suprimir expansões de shell. Se eu remover as aspas em torno de "$file" , minha saída será

mv -v -- a hello-world
mv -v -- c hello-world
mv -v -- f hello-world
grep: with: No such file or directory
grep: space: No such file or directory

Aqui está o script como um script:

#!/bin/bash

mkdir -p hello-world
for file in *; do
    if grep -q --exclude-dir=hello-world -- hello "$file"; then
        echo mv -v -- "$file" hello-world
    fi
done

Não se esqueça de remover echo quando estiver pronto para mover os arquivos para valer.

PS, pode haver maneiras mais eficientes de fazer isso. Meu caminho é apenas um exemplo.

    
por Zanna 01.02.2018 / 00:16
0

Esta seria uma solução seguindo sua ideia com grep e xargs:

#!/bin/bash
mkdir hello-world
grep -l hello * | xargs mv -t hello-world
    
por muclux 31.01.2018 / 13:17
-1

De um script bash chamado test.sh

#!/bin/sh
IFS=$'\n'
for i in $( grep -l hello --exclude=*.sh --exclude-dir=hello-world * ); do mv $i hello-world/; done;

Da linha de comando:

IFS=$'\n' && for i in $( grep -l hello --exclude-dir=hello-world * ); do mv $i hello-world/; done;

Espera que ajude você ...

Por favor, considere criar o primeiro diretório hello-world.

    
por Alvaro Niño 31.01.2018 / 13:05