2016-10-17 2 views
1

Я знаю, что моя следующая задача может быть выполнена с использованием более простого комментария «find», но я пытаюсь реализовать решение, используя рекурсивный вызов. Я просматриваю конкретный каталог и пытаюсь получить максимальную длину любого имени файла во всех подкаталогах. Однако моя рекурсия работает только на одном уровне вниз, поэтому она в основном возвращает мне самое длинное имя файла в определенном каталоге или в его «подкаталогах».Рекурсивная функция bash (итератор каталогов)

#! /bin/bash 

export maxlen=0 

findmaxr() 
{ 
     if [ $# -eq 0 ] ; then 
     echo "Please pass arguments. Usage: findmax dir" 
     exit -1 
     fi 

     if [ ! -d "$1" ];  then 
     echo "No such directory exist." 
     exit -2 
     fi 

     for file in $(/bin/ls $1) 
       do 
       if [ -d "$file" ] ; then 
         findmaxr $file # Recursively call the method for subdirectories 
       else 
         cur=${#file} 
         if [ $maxlen -lt $cur ] ; then 
           maxlen=$cur 
         fi 
       fi 
       done 

     echo "The file with the longest name has [$maxlen] characters." 



} 

findmaxr `pwd` 

ответ

2

Здесь есть несколько проблем. Самым большим является то, что в [ -d "$file" ] и findmaxr $file переменная file содержит только имя файла, а не весь путь. Итак, если вы находитесь в/dir и/dir/sub/sub2 существует, верхний уровень будет работать findmaxr /dir (это нормально), который будет работать findmaxr sub (который работает, но на самом деле это не то, что вы хотите), что затем запускается findmaxr sub2 (который вообще не работает). Таким образом, одна из возможностей заключается в использовании "$1/$file" в этих строках, но есть еще одна проблема: parsing ls is a bad idea and will fail for some directory names. Лучше было бы использовать for path in "$1"/*, у которого нет проблем с синтаксическим разбором (при условии, что вы дадите две цитаты всех ссылок на переменные, которые вам все равно) и дает вам полный путь, поэтому вам не нужно добавлять $1/. Но тогда вам нужно разделить переднюю часть пути, прежде чем проверять длину имени файла, с чем-то вроде local file="$(basename "$path")".

Вторая проблема заключается в том, что после окончания каждого каталога он печатает $maxlen, а не только после сканирования всего дерева. Чтобы исправить это, просто переместите эту команду из рекурсивной функции в основную последовательность, после того как она запустит findmaxr.

Там другая тонкая проблема, а также: file (cur и path, если вы будете следовать моей рекомендации) не являются локальными переменными, а это означает, рекурсивный вызов может изменить их значения ип вызывающего экземпляра findmaxr. Вы должны объявить их как локальную переменную, чтобы избежать путаницы. О, и нет необходимости экспортировать maxlen - он будет автоматически глобальным в сценарии, и это не относится ни к одной из команд, которые вы используете (что передает export).

+0

Спасибо. Я читал где-то еще, что разбор ls проблематичен, но не мог найти объяснения этого. Информация, которую вы предоставили, очень полезна! – DR29

2

Каждый экземпляр findmaxr должен вернуть maxlen, который он нашел (через echo), вам нужно включить этот вывод в ваше сравнение.

есть еще одна проблема: предположим a, b и c каталоги, d.txt файл, и они расположены так: a/b/c/d.txt. запустите сценарий изнутри a. когда file - b, if [ -d "$file" ] - это правда, и findmaxr b получает вызов. он вызывает ls b, что в свою очередь приводит к findmaxr c, что приводит к ls c ... кроме ls c не удается, потому что вы все еще в a. вам нужно либо cd в подкаталоги, либо включить весь путь, например: $1/$file.

вот ваш скрипт с этими проблемами фиксированной:

#!/bin/bash 

findmaxr() 
{ 
    if [ $# -eq 0 ] ; then 
    echo "Please pass arguments. Usage: findmax dir" 
    exit -1 
    fi 

    if [ ! -d "$1" ];  then 
    echo "No such directory exist." 
    exit -2 
    fi 

    maxlen=0 
    for file in $(/bin/ls "$1") 
      do 
      if [ -d "$1/$file" ] ; then 
        cur=`findmaxr "$1/$file"` # Recursively call the method for subdirectories 
      else 
        cur=${#file} 
      fi 
      if [ $maxlen -lt $cur ] ; then 
       maxlen=$cur 
      fi 
     done 
     echo $maxlen 
} 

maxlen=$(findmaxr $(pwd)) 
echo "The file with the longest name has [$maxlen] characters." 
Смежные вопросы