Ошибка сценария Bash со строками с путями, которые имеют пробелы и подстановочные знаки

25

У меня возникли проблемы с основами написания скриптов на Bash. Вот что у меня так далеко:

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

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

Все, что я хочу сделать, это перечислить все .txtфайлы в forцикле, чтобы я мог с ними что-то делать. Но пространство в my directoryзвездочке и в звездочке *.txtпросто не очень красиво. Я пытался использовать его с двойными кавычками и без них, с фигурными скобками на именах переменных и без них, но все еще не могу распечатать все .txtфайлы.

Это очень простая вещь, но я все еще борюсь, потому что я устал и не могу думать прямо.

Что я делаю не так?

Я смог успешно применить приведенный выше сценарий, если мои ФАЙЛЫ не имеют пробела или звездочки ... Мне пришлось экспериментировать с использованием или без использования двойных кавычек и фигурных скобок, чтобы заставить его работать. Но в тот момент, когда у меня есть и пробелы, и звездочка, все портится.

Джон
источник

Ответы:

33

Внутри кавычек, это *не будет расширяться до списка файлов. Чтобы успешно использовать такой шаблон, он должен быть вне кавычек.

Даже если подстановочный знак будет расширяться, выражение "${FILES}"будет приводить к одной строке, а не к списку файлов.

Один подход, который работал бы, был бы:

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

Выше имена файлов с пробелами или другими сложными символами будут обрабатываться правильно.

Более продвинутый подход может использовать массивы bash:

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

В данном случае FILESэто массив имен файлов. Парены, окружающие определение, делают его массивом. Обратите внимание, что за *пределами кавычек. Конструкция "${FILES[@]}"является особым случаем: она расширится до списка строк, где каждая строка является одним из имен файлов. Имена файлов с пробелами или другими сложными символами будут обрабатываться правильно.

John1024
источник
1
здорово, что сработало
Джон
Стоит отметить, что если вы передаете такие пути через функции, вам нужно убедиться, что вы заключили переменную в кавычки самостоятельно, а не объединяли ее как часть более крупной строки: for f in "$DIR"/*.txt= fine for f in "$DIR/*.txt"= breaks
pospi
7

Хотя использование массивов, показанных John1024, имеет гораздо больше смысла, здесь вы также можете использовать оператор split + glob (оставив скалярную переменную без кавычек).

Поскольку вам нужна только глобальная часть этого оператора, вам нужно отключить разделенную часть:

#! /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
Стефан Шазелас
источник
0

Что вы можете сделать, это оставить только символы подстановки за пределами кавычек.
Что-то вроде:
для a в "файлах с пробелами" * ". Txt"
сделать
обработку
выполненной.
Если сами подстановочные знаки расширяются до пробелов, то вам нужно будет использовать подход файла к строке, например, использовать ls -l для генерации списка файлы и использовать чтение bash, чтобы получить каждый файл.

Марсело Пачеко
источник
-1

Когда вы хотите обработать набор файлов, вы считаете, что в их имени может быть пробел или другой код scape, поэтому перед запуском процесса, например, for loopили find commandустановите IFS bash env variableдля:

IFS=$(echo -en "\n\b")
Персидский залив
источник