Verifique se uma pasta tem um certo tipo de arquivo presente

6

Como eu posso verificar se um arquivo tem um arquivo de extensão .java?

Até agora eu tenho:

  for javaFile in *.java ; do
    {
        echo "The file $javaFile has : " >> ~/Desktop/externalServers.txt
        grep -E '^[^/]{2}.*http' $javaFile >> ~/Desktop/externalServers.txt     

        grep -E '^[^/]{2}.*ftp' $javaFile  >> ~/Desktop/externalServers.txt

        echo "----------------------------------------" >> ~/Desktop/externalServers.txt
        sed -e "s/[[:space:]]\+/ /g" ~/Desktop/externalServers.txt >> ~/Desktop/externalServersTemp.txt
        rm ~/Desktop/externalServers.txt
        mv ~/Desktop/externalServersTemp.txt ~/Desktop/externalServers.txt
        sed 's/^\n//' ~/Desktop/externalServers.txt >> ~/Desktop/externalServersTemp.txt
        rm ~/Desktop/externalServers.txt
        mv ~/Desktop/externalServersTemp.txt ~/Desktop/externalServers.txt

    } done

Mas toda vez que faço isso, recebo o erro:

grep: *.java: No such file or directory

Basicamente eu quero primeiro ver se a pasta tem algum arquivo do tipo .java e só continuo com o script.

    
por gkmohit 02.06.2014 / 14:52

4 respostas

4

No Bash, simplesmente use

shopt -s nullglob
for pdfFile in *.java;do
    # your code goes here
done

Esta sintaxe é para shells semelhantes a Bourne; a opção nullglob é específica para o bash. As chaves que você usou ( {} ) são para shells estilo C.

shopt -s nullglob define a opção nullglob , que basicamente diz ao Bash que os globs que não corresponderem devem ser expandidos na string nula. Por padrão, se *.java não corresponder, ele será expandido para si mesmo (o asterisco permanecerá).

    
por 02.06.2014 / 14:55
4

Minha própria versão do seu script provavelmente seria:

set -- *.java
test -e "$1" && {
    fortyequals=$(printf '%040d\n' | tr 0 \=)
    for javaFile do
        printf '%s\nIn file: %s\n%s\n' \
            $fortyequals "$javaFile" $fortyequals
        grep -E '^[^/]{2}.*(ftp|http)' "$javaFile"       
    done 
} >>~/Desktop/externalservers.txt

A solução já oferecida é desnecessariamente específica do shell. Você poderia conseguir o mesmo efeito com a sintaxe portátil - o que faz com que menos se lembre a longo prazo com a vantagem adicional de ser mais robusto como:

set -- *.java
test -e "$1" &&
    for javaFile do
#       ...iterate on $javaFile here...
    done

Outra vantagem é que você não apenas retém o valor mais recente de $javaFile após o loop, mas também retém todos os valores que $javaFile tiveram em $@ . Isso possibilita o seguinte:

...
done

echo "The previous for loop processed $# files."

echo "The first file processed was:"
printf "///\t'%s'\t///\n" "$1"

echo "The last file processed was:"
printf "///\t'%s'\t///\n" "$javaFile"

echo 'All files processed in the for loop were:'
printf "///\t'%s'\t///\n" "$@"

E se você realmente gosta do { curlies } você pode usá-los - mesmo em bash (embora eles sejam desnecessários) - mas você tem que delimitar entre os dois shell palavras reservadas } e done como:

for ... do {
...
} ; done

Embora minha recomendação seja que você coloque todo o bloco - ao redor do loop for e qualquer pós-processamento que você fizer - em curlies dependentes das palavras reservadas && como:

set -- *.java
test -e "$1" && {
    for ... done
#   ...further processing on $@...
} 
Olhando para trás e acredito que posso ajudar bastante com o regex também ... Parece que estamos procurando por linhas contendo as palavras link e / ou ftp que não não começam com dois // .

Acho que o restante é resultado das operações grep separadas que você faz. Você parece estar tentando limpar linhas em branco, mas, como imagino, essas são causadas apenas em primeiro lugar por gravações anexadas repetidas no arquivo.

Então, em vez disso, poderíamos escrever apenas a saída de for loop diretamente no arquivo de saída para manter o descritor de gravação para ~/Desktop/externalservers.txt até que o loop tenha terminado, o que deve evitar que linhas em branco sejam gravadas. Talvez como:

for ... done >>outfile

Ou

{ grouped ; command ; list ; } >>outfile

No mínimo, posso dizer que essa declaração provavelmente não faz o que você quer:

sed 's/^\n//' $file

sed is \n ewline delimitado - é impossível encontrar um \n ewline como o caractere ^ primeiro em uma linha. Você pode obter \n ewlines no espaço padrão de sed por vários meios, mas nunca sem algum processamento.

    
por 03.06.2014 / 04:51
2

Outro problema com o código de exemplo, você geralmente deseja use aspas duplas em qualquer um dos iteradores conforme você os utiliza dentro do loop for.

Seu código:

for javaFile in *.java ;
    {
        echo "The file $javaFile has : " >> ~/Desktop/externalServers.txt
        grep -E '^[^/]{2}.*http' $javaFile >> ~/Desktop/externalServers.txt     

        grep -E '^[^/]{2}.*ftp' $javaFile  >> ~/Desktop/externalServers.txt

Deve ser:

for javaFile in *.java ; do
        echo "The file $javaFile has : " >> ~/Desktop/externalServers.txt
        grep -E '^[^/]{2}.*http' "$javaFile" >> ~/Desktop/externalServers.txt

        grep -E '^[^/]{2}.*ftp' "$javaFile"  >> ~/Desktop/externalServers.txt
    
por 02.06.2014 / 15:08
0

A abordagem nullglob de Joseph é a mais elegante, mas se você não quiser ou não puder usar isso (shell não-bash ou versão bash mais antiga, por exemplo), você também pode fazer isso (supondo que seus nomes de arquivo contenham sem novas linhas):

file="~/Desktop/externalServers.txt"
while IFS= read -r javaFile
do
  echo "The file $javaFile has : " >> "$file"
  grep -E '^[^/]{2}.*http' "$javaFile" >> "$file"
  grep -E '^[^/]{2}.*ftp' "$javaFile"  >> "$file"

  echo "----------------------------------------" >> "$file"
  ## The -i flag enables in-place editing so you don't need
  ## to fiddle about with temp files.
  sed -i -e "s/[[:space:]]\+/ /g" "$file"
  sed -i -n '/[^[:space:]]/p' "$file"
done < <(find . -maxdepth 1 -name '*.java')

O IFS= define o separador do campo de entrada como vazio para que você possa lidar com nomes de arquivos que contenham espaços corretamente e a opção -r para read significa que as barras invertidas não são tratadas especialmente (caso seus nomes possam conter ). A construção <(command) é chamada de substituição de processos e é uma maneira de passar saída de um comando como entrada para outro.

Eu introduzi a variável $file para que você não precise editar todas as linhas se quiser alterar o nome do arquivo de saída.

Observe que usei o sinalizador -i para sed que permite editar o arquivo original e remove a necessidade de arquivos temporários. Observe também que não há motivo para fazer rm foo.txt; mv bar.txt foo.txt , você sempre pode fazer apenas mv bar.txt foo.txt e isso sobrescreverá o arquivo de destino. Agora, não tenho ideia do que esse comando deveria fazer:

sed 's/^\n//' ~/Desktop/externalServers.txt >> ~/Desktop/externalServersTemp.txt

Eu estou supondo que você quer remover linhas em branco, mas isso não vai funcionar, então eu mudei o código acima com

sed -i -n '/[^[:space:]]/p' ~/Desktop/externalServers.txt

O -n suprime o comportamento padrão do sed de imprimir cada linha, o /[^[:space:]]/ corresponderá a qualquer linha que corresponda a qualquer caractere que não seja espaço e p no final significa que essas linhas e somente essas linhas serão impressas . Se isto não é o que você queria fazer com o seu comando sed , me avise e eu edito conforme necessário.

    
por 02.06.2014 / 17:41