Вопрос или проблема
В моем bash-скрипте я использую find, чтобы получить имена папок по шаблону:
for i in $(find ${directory} -mindepth 1 -type d -name ${wildcard});
do
stuff=doStuff ${i}
done
doStuff() {
echo ${1}
return ${1}'/hello';
}
Проблема в том, что когда я это делаю, я получаю следующую ошибку (предположим, что ${i} соответствует ‘home/me/my_directory’):
line 111: '/home/me/my_directory': is a directory.
(строка 111 — это строка с ‘doStuff’)
Я пробовал сделать это, но безуспешно:
for i in $(find ${directory} -mindepth 1 -type d -name ${wildcard});
do
stuff=doStuff "${i}"
done
Очевидно, это связано с тем, что программа пытается выполнить директорию, но я просто хочу, чтобы она была строкой, с которой я могу работать.
Кавычки и подстановка команд — это ваши проблемы здесь.
Конкретная проблема, с которой вы сталкиваетесь, связана с тем, что оболочка пытается выполнить команду с именем /home/me/my_directory
с переменной окружения stuff=doStuff
.
Что вы действительно хотите (если я правильно понимаю), так это запустить doStuff
с значением $i
в качестве аргумента, присваивая вывод переменной stuff
. Сделать это можно, заключив вашу команду в $()
. Например:
stuff="$(doStuff "$i")"
Обратите внимание, что я также обернул все в кавычки. Это нужно, чтобы предотвратить разбиение аргументов оболочкой на слова, что вы не хотите (это превратит единственный аргумент /foo/bar baz
в /foo/bar
и baz
).
Также вывод вашей функции является тем, что используется в качестве возвращаемого значения, а не return
.
Так как вы должны использовать больше кавычек, вы также должны добавить их ко всему остальному. Вот полная версия вашего скрипта:
doStuff() {
echo "${1}" >&2
printf '%s/hello' "$1";
}
IFS=$'\n'
for i in $(find "${directory}" -mindepth 1 -type d -name "${wildcard}");
do
stuff="$(doStuff "${i}")"
done
Вы должны определить функцию перед тем, как начнете ее использовать. Оболочка не разбирается так, как компилируемые программы, где она проходит через файл несколько раз. Она идет сверху вниз.
Я также изменил doStuff
, чтобы отправить echo
в STDERR, где он будет отображаться на терминале, а затем printf
отправляет в STDOUT, где он будет захвачен в переменную stuff
.
Имейте в виду, что это все равно вызовет проблему, если одна из ваших директорий содержит символ новой строки. Однако это ограничение оболочки. Единственный символ, который не может содержать путь к файлу, это NULL-символ (\0
). Однако bash и другие оболочки не могут хранить NULL-символ в строке (не все, но многие. Я знаю, что zsh может), поэтому вы не можете использовать его в качестве разделителя.
Это хорошая попытка, но она показывает, что вы пытаетесь писать bash
так, как если бы это был язык программирования, хотя на самом деле это простой язык скриптов. Проблема с вашей конструкцией stuff=doStuff arg
в том, что для bash
это не значит то, что вы думаете. Для bash
, stuff=doStuff
— это присваивание переменной, а arg
— команда. Я предлагаю вам прочитать следующие две ссылки:
- Как я могу сохранить значение возврата и/или вывод команды в переменной?
- Как вернуть строку (или большое число, или отрицательное число) из функции? “return” позволяет мне давать только число от 0 до 255.
Рабочая версия чего-то похожего на то, что у вас есть:
doStuff () {
echo "$1/hello"
}
stuff=$(doStuff "$i")
Строка, вызывающая ошибку, не делает то, что вы думаете:
Сначала она устанавливает переменную окружения stuff в строку “doStuff”, а затем пытается выполнить ${i} как команду. И так как ${i} является директорией, это завершается с указанным сообщением об ошибке.
У меня была такая же проблема. В моей команде запуска я использовал {{release_or_current_path}}, чтобы получить директорию. И это тоже дало мне ту же ошибку. Я исправил это, добавив cd и разделив команду с помощью &&.
Ответ или решение
В вашем сценарии с использованием Bash возникает ошибка “is a directory”, когда вы пытаетесь передать имя директории в функцию. Чтобы разобраться в данной проблеме, нужно уточнить несколько моментов и исправить определённые ошибки в коде.
Анализ проблемы
Когда вы вызываете функцию doStuff
с переменной ${i}
, Bash истолковывает строку stuff=doStuff ${i}
как попытку присвоить значение переменной stuff
. В этом выражении:
stuff=doStuff
указывает на создание переменнойstuff
со значениемdoStuff
.- Затем Bash пытается выполнить
${i}
как команду, которая, в случае если${i}
ссылается на директорию, приводит к ошибке, что директория не может быть выполнена.
Правильный способ вызова функции
Чтобы корректно вызвать функцию и сохранить её результат, необходимо будет использовать обратные кавычки или конструкцию $()
. Вот как правильно можно адаптировать ваш код:
doStuff() {
echo "$1/hello"
}
IFS=$'\n' # Установка разделителя входных данных на новую строку, чтобы избежать проблем с пробелами в именах файлов
for i in $(find "${directory}" -mindepth 1 -type d -name "${wildcard}"); do
stuff=$(doStuff "$i") # Правильный способ вызова функции и сохранения результата
echo "$stuff" # Выводим или используем полученное значение
done
Объяснение изменений
-
Использование
$()
для вызова функции: Эта конструкция позволяет передать вывод функции (результат её выполнения) в переменную. В данном случаеstuff
будет содержать вывод функцииdoStuff
. -
Передача параметров в функцию: Обертывание
"${i}"
в кавычки важно для правильной обработки имен директорий, которые могут содержать пробелы или специальные символы. Это предотвратит потенциальные ошибки при обработке таких имен. -
Output функции: В функции
doStuff
происходит возврат строки с помощьюecho
, что позволяет захватить этот вывод в переменнойstuff
.
Обращение с ошибками
Если в ваших директориях могут присутствовать символы, приводящие к проблемам с разделением строк (например, символы новой строки), вы можете столкнуться с тем, что find
будет выдавать многострочные результаты. В этом случае использование read
с циклом будет более безопасным вариантом:
find "${directory}" -mindepth 1 -type d -name "${wildcard}" | while IFS= read -r i; do
stuff=$(doStuff "$i")
echo "$stuff"
done
Заключение
Получение и обработка директорий в Bash могут быть деликатными, особенно когда речь идет о специальных символах. Правильное использование строковых литералов, функций и возвратов значений значительно улучшает надёжность вашего кода. С учетом этих рекомендаций вы сможете внести необходимые изменения в свой сценарий и успешно работать с директориями и их именами.