Erro de script bash com strings com caminhos que possuem espaços e caracteres curinga

24

Estou tendo problemas para entender os fundamentos do Bash. Aqui está o que eu tenho até agora:

#!/bin/bash
FILES="/home/john/my directory/*.txt"

for f in "${FILES}"
do
  echo "${f}"
done

Tudo o que quero fazer é listar todos os arquivos .txt em um loop for para que eu possa fazer coisas com eles. Mas o espaço no my directory e o asterisco em *.txt simplesmente não estão funcionando bem. Tentei usá-lo com e sem aspas duplas, com e sem chaves nos nomes das variáveis e ainda não consigo imprimir todos os arquivos .txt .

Isso é muito básico, mas ainda estou lutando porque estou cansado e não consigo pensar direito.

O que estou fazendo de errado?

Eu consegui aplicar o script acima com sucesso se meus ARQUIVOS não tiverem espaço ou um asterisco ... Tive que experimentar com ou sem o uso de aspas duplas e chaves para que ele funcionasse. Mas no momento em que tenho os dois espaços e um asterisco, isso estraga tudo.

    
por John 20.09.2014 / 06:59

3 respostas

30

Dentro de aspas, o * não será expandido para uma lista de arquivos. Para usar esse curinga com sucesso, ele deve estar fora das cotações.

Mesmo se o curinga fosse expandido, a expressão "${FILES}" resultaria em uma única string, não em uma lista de arquivos.

Uma abordagem que funcionaria seria:

#!/bin/bash
DIR="/home/john/my directory/"
for f in "$DIR"/*.txt
do
  echo "${f}"
done

Acima, nomes de arquivos com espaços ou outros caracteres difíceis serão tratados corretamente.

Uma abordagem mais avançada poderia usar matrizes bash:

#!/bin/bash
FILES=("/home/john/my directory/"*.txt)
for f in "${FILES[@]}"
do
  echo "${f}"
done

Nesse caso, FILES é uma matriz de nomes de arquivos. Os parentes que cercam a definição fazem dela uma matriz. Observe que o * está fora das cotações. A construção "${FILES[@]}" é um caso especial: ela será expandida para uma lista de cadeias em que cada cadeia é um dos nomes de arquivos. Nomes de arquivos com espaços ou outros caracteres difíceis serão tratados corretamente.

    
por 20.09.2014 / 07:05
7

Embora o uso de matrizes como mostrado por John1024 faça muito mais sentido, aqui você também pode usar o operador split + glob (deixando uma variável escalar sem aspas).

Como você deseja apenas a parte glob desse operador, é necessário desativar a parte split :

#! /bin/sh -
# that also works in any sh, so you don't even need to have or use bash

file_pattern="/home/john/my directory/*.txt"
# all uppercase variables should be reserved for environment variables

IFS='' # disable splitting

for f in $file_pattern # here we're not quoting the variable so
                       # we're invoking the split+glob operator.
do
  printf '%s\n' "$f" # avoid the non-reliable, non-portable "echo"
done
    
por 20.09.2014 / 15:58
-1

Quando você deseja processar em um conjunto de arquivos, você considera, em seu nome pode haver espaço ou outro código de escape, Então, antes de iniciar seu processo, como for loop ou find command , defina o IFS bash env variable como :

IFS=$(echo -en "\n\b")
    
por 20.09.2014 / 13:57