Ниже приведена одна из возможных реализаций:
# my_ls -- recursively list given directory's contents and subdirectories
# $1=directory whose contents to list
# $2=indentation when listing
my_ls() {
# save current directory then cd to "$1"
pushd "$1" >/dev/null
# for each non-hidden (i.e. not starting with .) file/directory...
for file in * ; do
# print file/direcotry name if it really exists...
test -e "$file" && echo "$2$file"
# if directory, go down and list directory contents too
test -d "$file" && my_ls "$file" "$2 "
done
# restore directory
popd >/dev/null
}
# recursively list files in current
# directory and subdirectories
my_ls .
В качестве упражнения вы можете подумать о том, как изменить приведенный выше сценарий для печати полных путей к файлам (вместо просто с отступом имени файла / каталогов), возможно, избавившись от pushd
/ popd
(и от необходимости второй параметр $2
) в процессе.
Кстати, обратите внимание на использование test XYZ && command
, которое полностью эквивалентно if test XYZ ; then command ; fi
(т. Е. Выполнить command
, если test XYZ
успешно). Также отметим, что test XYZ
эквивалентно [ XYZ ]
, то есть вышеупомянутое также эквивалентно if [ XYZ ] ; then command ; fi
. Также обратите внимание, что любую точку с запятой ;
можно заменить новой строкой, они эквивалентны.
Удалите условие test -e "$file" &&
(оставьте только echo
) и посмотрите, что произойдет.
Удалите двойные кавычки вокруг "$file"
и посмотрите, что произойдет, если каталог, содержимое которого вы видите, содержит имена файлов с пробелами в них. Добавьте set -x
вверху скрипта (или вместо этого вызовите его как sh -x scriptname.sh
), чтобы включить вывод отладки и посмотреть, что происходит подробно (чтобы перенаправить вывод отладки в файл, запустите sh -x scriptname.sh 2>debugoutput.txt
).
Для просмотра списка скрытых файлов (например, .bashrc
):
...
for file in * .?* ; do
if [ "$file" != ".." ] ; then
test -e ...
test -d ...
fi
done
...
Обратите внимание на использование !=
(сравнение строк) вместо -ne
(числовое сравнение.)
Другая техника - порождение подпочв вместо использования pushd
/ popd
:
my_ls() {
# everything in between roundbrackets runs in a separatly spawned sub-shell
(
# change directory in sub-shell; does not affect parent shell's cwd
cd "$1"
for file in ...
...
done
)
}
Обратите внимание, что в некоторых реализациях оболочки существует жесткое ограничение (~ 4 КБ) на количество символов, которые могут быть переданы в качестве аргумента for
(или любой встроенной или внешней команде в этом отношении.) Поскольку Оболочка расширяет встроенный *
список всех совпадающих имен файлов перед тем, как фактически выполнить над ним for
, вы можете столкнуться с проблемами, если *
развернут внутри каталога с большим количеством файлов ( та же проблема, с которой вы столкнетесь при запуске, скажем, ls *
в том же каталоге, например, получите сообщение об ошибке, например Command too long
.)