Как найти файл по $PATH в Bash?

#bash #path #posix

#bash #путь #posix

Вопрос:

Утилита which найдет программу по указанному пути, но как насчет произвольного файла данных?

Должно быть, я ищу не тот материал, но я не могу найти никакого способа сделать это с помощью стандартной утилиты, поэтому я подумал о написании небольшого скрипта Bash для этого.

$PATH Переменная среды разделяется двоеточиями, но попытка установить IFS=':' , а затем выполнить итерацию по результатам, у меня не работает. Вот что у меня получилось на данный момент:

 IFS=':' DIRS=($PATH)
for d in $DIRS; do echo $d; done
  

На данный момент он выводит только первую запись в моем path, а не все из них.

Мысли? Если уже есть стандартная команда, которая делает это, тогда нет смысла писать скрипт …

Комментарии:

1. Произвольные файлы данных, вероятно, не должны находиться в списках каталогов в PATH .

Ответ №1:

Используя это, используя расширения параметров bash и find :

 find ${PATH//:// } -name 'file'
  

Комментарии:

1. Предположим, что в пути есть каталог, в котором есть пробел. Или файл в каталоге по ПУТИ с пробелом в имени файла.

2. Вы также не расширяете файлы в последней записи ПУТИ.

3. Это решение было бы здорово, если бы я мог использовать find таким образом, чтобы выполнялся поиск $PATH , но замена двоеточий пробелами не сработает

4. @GillesQuenot Я поддержал ваше find решение, потому что оно мне нравится, но оно не будет работать, когда в каталогах, которые находятся в $PATH

5. Я никогда не использую пробелы, особенно для каталога PATH, и мне тоже нравится мое решение ^^

Ответ №2:

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

 IFS=: dirs=($PATH)
for dir in "${dirs[@]}"; do
    echo "$dir"
done
  

у меня работает; или более кратко

 printf '%sn' "${dirs[@]}"
  

Как вы обнаружили, just $dirs возвращает только первый элемент из массива.

Чтобы на самом деле обойти эти каталоги в поисках определенного файла, возможно, попробуйте

 desired_file_name=$1  # or whatever
for dir in "${dirs[@]}"; do
    test -e "$dir/$desired_file_name" || continue
    echo "$0: found $dir/$desired_file_name" >amp;2
done
  

Другой подход заключается

 find "${dirs[@]}" -name "$desired_file_name"
  

(Вы должны предпочитать нижний регистр для своих личных переменных, поэтому я изменил DIRS на dirs . Меньше криков тоже полезно для глаз.)

Комментарии:

1. Ха, похоже, я не проверил то, что написал.

Ответ №3:

Здесь происходит несколько вещей:

  1. IFS=':' DIRS=($PATH)

    bash развернет переменную перед установкой IFS и DIRS, поэтому к тому времени будет слишком поздно. Вам понадобится что-то сумасшедшее, например

     dirs=()
    while IFS= read -r -d: dir || [ "$dir" ]; do dirs =("$dir"); done <<<"$PATH"
      


— нет, это было неправильно. Сначала расширяется переменная $PATH, затем устанавливается IFS, затем элемент DIRS разделяется с использованием IFS, поскольку он не заключен в кавычки.

  1. for d in $DIRS; do echo $d; done

    Чтобы выполнить итерацию по всем элементам массива, вам нужно

     for d in "${dirs[@]}" ...
      

    С массивом, $DIRS равным ${DIRS[0]} , то есть первой записи.

  2. Не используйте ALLCAPS varnames. Слишком просто перезаписать важную системную переменную.

  3. Заключайте свои переменные в кавычки, если вы не знаете точно, что произойдет, если вы этого не сделаете.

Ответ №4:

Чтобы найти файл с именем $name на вашем PATH компьютере, используйте цикл bash:

 oldIFS=$IFS
IFS=:
for d in $PATH
do
    [ -f "$d/$name" ] amp;amp; echo "Found $d/$name"
done
IFS=$oldIFS
  

Во многих системах, таких как debian, which это просто сценарий оболочки, и он использует очень похожий цикл. Взгляните на less /usr/bin/which .