Erro de script bash com sequências com caminhos que possuem espaços e curingas

25

Estou tendo problemas para entender o básico dos scripts 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 eu quero fazer é listar todas as .txt arquivos em um forloop para que eu possa fazer coisas com eles. Mas o espaço no my directorye o asterisco *.txtsimplesmente 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 .txtarquivos.

Isso é uma coisa muito básica, mas ainda estou lutando porque estou cansada e não consigo pensar direito.

O que estou fazendo errado?

Consegui aplicar com êxito o script acima se meus ARQUIVOS não tiverem espaço ou asterisco ... tive que experimentar com ou sem o uso de aspas duplas e chaves para fazê-lo funcionar. Mas no momento em que tenho espaços e um asterisco, tudo estraga tudo.

John
fonte

Respostas:

33

Entre aspas, o * texto não será expandido para uma lista de arquivos. Para usar esse curinga com sucesso, ele deve estar fora das aspas.

Mesmo que o curinga se expanda, a expressão "${FILES}" resultaria em uma única sequência, 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. As parênteses em torno da definição a tornam uma matriz. Observe que *está fora das aspas. A construção "${FILES[@]}"é um caso especial: será expandida para uma lista de cadeias onde cada cadeia é um dos nomes de arquivo. Os nomes de arquivos com espaços ou outros caracteres difíceis serão tratados corretamente.

John1024
fonte
11
grande que trabalhou
John
É importante notar que, se você está passando caminhos como este ao redor através de funções, você precisa garantir que você citar a variável por conta própria , em vez de concatenar-lo como parte de uma cadeia maior: for f in "$DIR"/*.txt= fina for f in "$DIR/*.txt"= breaks
pospi
7

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

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

#! /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
Stéphane Chazelas
fonte
0

O que você pode fazer é deixar apenas os caracteres curinga fora das aspas.
Algo como:
um em "arquivos com espaços" * "txt"
que
o processamento
feito
Se os próprios wildcards expandir a espaços, então você precisa t usar um arquivo por abordagem de linha, como o uso ls -l para gerar a lista de arquivos e use o bash read para obter cada arquivo.

Marcelo Pacheco
fonte
-1

Quando você deseja processar um conjunto de arquivos, considere que, em seu nome, pode haver espaço ou outro código scape estará presente. Portanto, antes de iniciar seu processo, como for loopou find commanddefina o seguinte IFS bash env variable:

IFS=$(echo -en "\n\b")
PersianGulf
fonte