Error de secuencia de comandos Bash con cadenas con rutas que tienen espacios y comodines

25

Tengo problemas para entender los conceptos básicos de Bash. Esto es lo que tengo hasta ahora:

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

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

Todo lo que quiero hacer es enumerar todos los .txtarchivos en un forbucle para poder hacer cosas con ellos. Pero el espacio en el my directoryy el asterisco en *.txtsimplemente no están jugando bien. Intenté usarlo con y sin comillas dobles, con y sin llaves en nombres de variables y todavía no puedo imprimir todos los .txtarchivos.

Esto es algo muy básico, pero todavía estoy luchando porque estoy cansado y no puedo pensar con claridad.

¿Qué estoy haciendo mal?

He podido aplicar con éxito la secuencia de comandos anterior si mis ARCHIVOS no tienen un espacio o un asterisco ... Tuve que experimentar con o sin comillas dobles y llaves para que funcione. Pero en el momento en que tengo ambos espacios y un asterisco, lo estropea todo.

John
fuente

Respuestas:

33

Entre comillas, *no se expandirá a una lista de archivos. Para utilizar dicho comodín con éxito, debe estar fuera de las comillas.

Incluso si el comodín se expandiera, la expresión "${FILES}"daría como resultado una sola cadena, no una lista de archivos.

Un enfoque que funcionaría sería:

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

En lo anterior, los nombres de archivo con espacios u otros caracteres difíciles se manejarán correctamente.

Un enfoque más avanzado podría usar matrices bash:

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

En este caso, FILESes una matriz de nombres de archivo. Los parens que rodean la definición lo convierten en una matriz. Tenga en cuenta que *está fuera de las comillas. La construcción "${FILES[@]}"es un caso especial: se expandirá a una lista de cadenas donde cada cadena es uno de los nombres de archivo. Los nombres de archivo con espacios u otros caracteres difíciles se manejarán correctamente.

John1024
fuente
1
genial que funcionó
John
Vale la pena señalar que si está pasando rutas como esta a través de funciones, debe asegurarse de citar la variable por sí misma en lugar de concatenarla como parte de una cadena más grande: for f in "$DIR"/*.txt= fine for f in "$DIR/*.txt"= breaks
pospi
7

Si bien el uso de matrices como lo muestra John1024 tiene mucho más sentido, aquí también puede usar el operador split + glob (dejando una variable escalar sin comillas).

Como solo desea la parte global de ese operador, debe deshabilitar la 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
fuente
0

Lo que puede hacer es dejar solo los caracteres comodín fuera de las comillas.
Algo así como:
para un en "archivos con espacios" * ". Txt"
haga el
procesamiento
hecho
Si los comodines se expanden a espacios, entonces necesitará usar un enfoque de archivo por línea, como usar ls -l para generar la lista de archivos y use bash read para obtener cada archivo.

Marcelo Pacheco
fuente
-1

Cuando desee procesar en un conjunto de archivos, considere que, en su nombre, puede haber espacio u otro código de escape estará allí, por lo tanto, antes de comenzar su proceso, for loopo find commandconfigure el IFS bash env variable:

IFS=$(echo -en "\n\b")
Golfo pérsico
fuente