ошибка ‘это каталог’ при попытке передать имя каталога в функцию

Вопрос или проблема

В моем 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 — команда. Я предлагаю вам прочитать следующие две ссылки:

Рабочая версия чего-то похожего на то, что у вас есть:

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

Объяснение изменений

  1. Использование $() для вызова функции: Эта конструкция позволяет передать вывод функции (результат её выполнения) в переменную. В данном случае stuff будет содержать вывод функции doStuff.

  2. Передача параметров в функцию: Обертывание "${i}" в кавычки важно для правильной обработки имен директорий, которые могут содержать пробелы или специальные символы. Это предотвратит потенциальные ошибки при обработке таких имен.

  3. 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 могут быть деликатными, особенно когда речь идет о специальных символах. Правильное использование строковых литералов, функций и возвратов значений значительно улучшает надёжность вашего кода. С учетом этих рекомендаций вы сможете внести необходимые изменения в свой сценарий и успешно работать с директориями и их именами.

Оцените материал
Добавить комментарий

Капча загружается...