Substituição de comando em loop não funcionando

3

Eu quero manter todos os arquivos não terminando com .bat

Eu tentei

for f in $(ls | egrep -v .bat); do echo $f; done

e

for f in $(eval ls | egrep -v .bat); do echo $f; done

Mas ambas as abordagens produzem o mesmo resultado, pois imprimem tudo. Considerando que ls | egrep -v .bat e eval ls | egrep -v .bat funcionam per se, se usados separadamente do loop for .

EDITAR

É interessante ver que, se eu deixar de fora o -v flag, o loop faz o que deveria e lista todos os arquivos que terminam em .bat .

Sinta-se à vontade para editar o título da pergunta, pois eu não sabia qual era o problema.

Estou usando GNU bash, version 4.1.10(4)-release (i686-pc-cygwin).

EXEMPLO

$ ls -l | egrep -v ".bat"
total 60K
-rwx------+ 1 SYSTEM SYSTEM 5.3K Jun  6 20:31 fsc*
-rwx------+ 1 SYSTEM SYSTEM 5.3K Jun  6 20:31 scala*
-rwx------+ 1 SYSTEM SYSTEM 5.3K Jun  6 20:31 scalac*
-rwx------+ 1 SYSTEM SYSTEM 5.3K Jun  6 20:31 scaladoc*
-rwx------+ 1 SYSTEM SYSTEM 5.3K Jun  6 20:31 scalap*

O comando está funcionando, mas não no loop for.

$ for f in $(ls | egrep -v .bat); do echo $f; done
fsc
fsc.bat
scala
scala.bat
scalac
scalac.bat
scaladoc
scaladoc.bat
scalap
scalap.bat
scalac
scalac.bat
scaladoc
scaladoc.bat
scalap
scalap.bat

DEBUG $ set -x

mike@pc /cygdrive/c/Program Files (x86)/scala/bin
$ for f in $(ls | egrep -v .bat); do echo $f; done
++ ls -hF --color=tty
++ egrep --color=auto -v .bat
+ for f in '$(ls | egrep -v .bat)'
+ echo fsc
fsc
+ for f in '$(ls | egrep -v .bat)'
+ echo fsc.bat
fsc.bat
// and so on
    
por mike 26.08.2013 / 17:25

3 respostas

6

Algumas coisas estão erradas no seu código:

Usando a substituição de comando sem aspas ( $(...) ) sem definir $IFS

Deixar expansões sem aspas é o operador split + glob. O padrão é dividir no espaço, na guia e na nova linha. Aqui, você só quer dividir em nova linha, então você precisa definir o IFS para que, caso contrário, isso significa que não funcionará corretamente se os nomes de arquivos contiverem espaços ou caracteres de tabulação

Usando a substituição de comando sem aspas, sem set -f .

Deixar expansões sem aspas é o operador split + glob. Aqui você não quer globbing, que é a expansão de curingas como scala* na lista de arquivos correspondentes. Quando você não quer que o shell faça globbing, você tem que desabilitá-lo com set -f

ls aliased para ls -F

O problema acima é agravado pelo fato de você ter ls aliased para ls -F . Que adiciona / aos diretórios e * aos arquivos executáveis. Portanto, normalmente, porque scala é executável, ls -F outputs scala* e como um padrão globbing, ele é expandido para todos os nomes de arquivos que começam com scala , o que explica porque parece que egrep -v não está filtrando arquivos fora.

Supondo que nomes de arquivo não contenham caracteres de nova linha

newline é um caractere tão válido quanto qualquer outro em um nome de arquivo. Portanto, analisar a saída de ls normalmente não funciona . Por exemplo, a saída de ls em um diretório que contém a e b files é a mesma que em um diretório que contém um arquivo chamado a\nb .

Acima de egrep filtrará as linhas dos nomes de arquivos, não os nomes de arquivos

Usando egrep em vez de grep -E

egrep está obsoleto. grep -E é o equivalente padrão.

Não está escapando do operador . regex.

Acima, você usou egrep para ativar expressões regulares estendidas , mas não usa nenhum dos operadores específicos de RE estendidos. O único operador de RE que você está usando é . para corresponder a qualquer caractere, embora pareça que não é isso que você pretendia. Então você pode também ter usado grep -F aqui. Ou use grep -v '\.bat' .

Não ancora o regexp no fim de linha

egrep .bat corresponderá a qualquer linha que contenha qualquer caractere seguido por bat , então esse é o regexp que significa qualquer coisa que contenha bat não na primeira posição. Deve ter sido grep -v '\.bat$' .

Deixando $f sem aspas

Deixar uma expansão sem aspas é o operador split + glob. Lá, você não quer nem, então $f deve ser citado ( "$f" ).

Use echo

echo expande as sequências de escape ANSI C nos seus argumentos e / ou trata cadeias como -n ou -e especialmente dependendo da implementação echo (e / ou do ambiente).

Use printf .

Então, uma solução melhor:

for f in *; do
  case $f in
    (*.bat);;
    (*) printf '%s\n' "$f"
  esac
done

No entanto, se não houver nenhum arquivo não oculto no diretório atual, isso ainda gerará * . Você pode contornar isso em zsh alterando * para *(N) ou em bash executando shopt -s nullglob .

    
por 26.08.2013 / 19:43
8

Você não deve usar ls para analisar arquivos dessa maneira.

Primeiro defina globstar extglob por shopt -s extglob globstar then

for f in !(*.bat)
  do
   printf '%s\n' "$f"
  done

Usando find

find . -type f ! -name '*.bat' 

Use o operador de negação para um tratamento mais seguro dos arquivos.

    
por 26.08.2013 / 17:37
1

Não há nada de errado com o seu loop:

$ for f in $(ls | egrep -v .bat); do echo $f; done

Aqui está minha saída para o comando acima:

$ for f in $(ls | egrep -v .bat); do echo $f; done
fsc
scala
scalac
scaladoc
scalap

Correção possível

Uma sugestão seria citar o argumento para egrep , assim:

$ for f in $(ls | egrep -v '.bat'); do echo $f; done

Também dê uma olhada no wiki do Bash Pitfalls para outros possíveis problemas ao criar scripts de coisas no Bash como essa. Em particular, isso soa como a razão mais plausível por que você está encontrando esse problema. Verifique se algum dos seus arquivos que estão sendo retornados contém espaços.

Depuração

Outra coisa é tentar habilitar a depuração detalhada do seu comando bash, antes de executá-lo para que você possa ver o que está acontecendo nos bastidores.

Isso permite a depuração:

$ set -x

Em seguida, execute seu comando:

$ for f in $(ls | egrep -v .bat); do echo $f; done
++ ls --color=auto
++ egrep --color=auto -v .bat
+ for f in '$(ls | egrep -v .bat)'
+ echo fsc
fsc
+ for f in '$(ls | egrep -v .bat)'
+ echo scala
scala
+ for f in '$(ls | egrep -v .bat)'
+ echo scalac
scalac
+ for f in '$(ls | egrep -v .bat)'
+ echo scaladoc
scaladoc
+ for f in '$(ls | egrep -v .bat)'
+ echo scalap
scalap

Em seguida, desative:

$ set +x

Emitir com o Cygwin?

Dado que os exemplos funcionam bem em vários sistemas Linux nativos, o problema está provavelmente relacionado a ter algo a ver com o Cygwin e / ou suas versões específicas de bash e egrep . Eu daria uma atenção especial ao separador de campo no Bash, $IFS para ver se há um problema entre os vários separadores de linha no Windows ( 0x0d,0x0a ) vs. Unix ( 0x0a ).

Eu não tenho uma instalação do Cygwin, então não tenho um método para provar essa hipótese.

    
por 26.08.2013 / 18:26