Я пишу простую рекурсивную программу ls в bash (в которой я очень неопытен, так что не стесняйтесь быть жестоким).
Программа должна распечатывать каждый файл (возможно, каталог) в отдельной строке, и каждый раз, когда вводится новый каталог, вывод сдвигается на 4 пробела, чтобы получить древовидный вывод.
В настоящее время он неправильно распечатывает файлы с пробелами и не ставит косую черту после каталогов. (Подробнее ниже.)
Код
recls () {
# store current working directory
# issues: seems bad to have cwd defined up here and used down below in getAbsolutePath -- too much coupling
cwd=$PWD
# get absolute path of arg
argdir=`getAbsolutePath "$@"`
# check if it exists
if [ ! -e $argdir ]; then
echo "$argdir does not exist"
return 1
fi
echo "$argdir exists"
# check if it's a directory
if [ ! -d $argdir ]; then
echo "$argdir is not a directory"
return 2
fi
echo "$argdir is a directory"
tab=""
recls_internal $argdir
return 0
}
recls_internal () {
for file in $@; do
echo -n "$tab${file##/*/}"
if [ -d $file ]; then
# print forward slash to show it's a directory
echo "/"
savedtab=$tab
tab="$tab "
myls_internal $file/*
tab=$savedtab
else
# if not a directory, print a new line
echo ""
fi
done
}
getAbsolutePath () {
if [ -z ${1##/*} ]; then
echo "$1"
else
echo "$cwd/$1"
fi
}
Выход
Сценарий находится в папке с именем bash-practice
. Когда я делаю recls .
, я получаю следующий вывод:
./
myls.sh
myls.sh~
recdir.sh
recls.sh
recls.sh~
sample
document.txt
sample-folder
sample-stuff
test-12.txt
test-1.txt
test-2.txt
sort-test.txt
sort-text-copy.txt
test-5-19-14-1
Проблема
Как видите, отступ работает правильно, но есть две проблемы:
1) Файл sample document.txt
разбит на две строки, потому что в нем есть пробел.
2) Каждый каталог должен иметь перед собой косую черту, но по какой-то причине это работает только для самого первого.
Попытка решения
Чтобы исправить (1), я попытался сохранить внутренний разделитель файлов и заменить его символом новой строки следующим образом:
...
tab=""
savedIFS=$IFS
IFS="\n"
recls_internal $argdir
IFS=$savedIFS
return 0
Но это совсем не сработало. Он даже не отображал больше, чем первая папка. Ясно, что мое понимание вещей неверно.
Что касается (2), я не вижу причин, по которым он не должен работать должным образом.
Вывод
bash сложен для меня, так как он, кажется, имеет более необычный синтаксис, чем большинство других языков программирования (язык сценариев оболочки), поэтому я был бы признателен за любое понимание моих ошибок, а также за решение.
Обновление №1
Я зашел на сайт http://www.shellcheck.com, который предложил mklement0, и практически все его подсказки двойные кавычки. Когда я дважды процитировал "$@"
, программа правильно напечатала файл sample document.txt
, но сразу после этого выдала мне ошибку "binary operator expected
". Вот распечатка того, как это выглядит сейчас:
Обновление №2 [проблема решена?]
Хорошо, оказалось, что у меня была опечатка, из-за которой по умолчанию использовалась более ранняя версия моей функции с именем myls_internal
, когда она рекурсивно. Эта более ранняя версия не помечала каталоги косой чертой. Также было исправлено сообщение об ошибке в разделе «Обновление». я изменил строку
myls_internal "$file/*"
to
recls_internal $file/*
и теперь он, кажется, работает правильно. Если кто-то находится в процессе написания ответа, я все равно ценю ваше понимание, поскольку я не совсем понимаю механизм того, как цитирование «$ @» решило проблему с пробелами.
Фиксированный код:
recls () {
# store current working directory
# issues: seems bad to have cwd defined up here and used down below in getAbsolutePath -- too much coupling
cwd=$PWD
# get absolute path of arg
argdir=$(getAbsolutePath "$@")
# check if it exists
if [ ! -e $argdir ]; then
echo "$argdir does not exist"
return 1
fi
echo "$argdir exists"
# check if it's a directory
if [ ! -d $argdir ]; then
echo "$argdir is not a directory"
return 2
fi
echo "$argdir is a directory"
tab=""
recls_internal $argdir
return 0
}
recls_internal () {
for file in "$@"; do
echo -n "$tab${file##/*/}"
if [ -d "$file" ]; then
# print forward slash to show it's a directory
echo "/"
savedtab=$tab
tab="$tab "
recls_internal $file/*
tab=$savedtab
else
# if not a directory, print a new line
echo ""
fi
done
}
getAbsolutePath () {
if [ -z ${1##/*} ]; then
echo "$1"
else
echo "$cwd/$1"
fi
}
Фиксированный вывод:
Обновление №3
Линия
recls_internal $file/*
вместо этого должно быть
recls_internal "$file"/*
который правильно обрабатывает каталоги с пробелами в них. В противном случае такая папка, как cs 350
, содержащая Homework1.pdf
и Homework2.pdf
, расширится до
cs 350/Homework1.pdf 350/Homework2.pdf
когда это должно быть
cs 350/Homework1.pdf cs 350/Homework2.pdf
Я думаю? Я действительно не понимаю более тонких деталей того, что происходит, но это, казалось, исправило это.
ls
. Вы увидите, что я никогда не вызываю ничего, кромеecho
и моей собственной функцииrecls
. И все же спасибо за ссылку. - person Chris Middleton   schedule 20.05.2014[[ ... ]]
.) - person Jonathan Leffler   schedule 20.05.2014