Я знаю, что можно использовать find
команда для этого простого задания, но я заставил присвоение не использовать find
или ls
и сделайте задание. Как я могу сделать это?
Вы можете сделать это только с Shell
#!/bin/bash
recurse() {
for i in "$1"/*;do
if [ -d "$i" ];then
echo "dir: $i"
recurse "$i"
elif [ -f "$i" ]; then
echo "file: $i"
fi
done
}
recurse /path
или если у вас есть Bash 4.0
#!/bin/bash
shopt -s globstar
for file in /path/**
do
echo $file
done
$ function f { for i in $1/*; do if [ -d $i ]; then echo $i; f $i; fi; done }
$ mkdir -p 1/2/3 2/3 3
$ f .
./1
./1/2
./1/2/3
./2
./2/3
./3
Мне нравится Mark Byers, вы можете использовать echo *
, чтобы получить список всех файлов в текущем каталоге.
Тест
или []
команда / встроенный вариант для проверки, если файл является каталогом.
Применить рекурсию, и вы закончите.
Команда du
рекурсивно перечисляет подкаталоги.
Я не уверен, что пустые каталоги могут упомянуть, хотя
Технически, ни find, ни ls не используются find2perl| perl или File::Find напрямую.
$ find2perl -type d | perl $ perl -MFile::Find -e'find(sub{-d&&print"$File::Find::name\n"},".")'
Ниже приводится одна из возможных реализаций:
# 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 &&
, которая полностью эквивалентна , если test XYZ; затем команда; fi
(т.е. выполнить команду
, если проверка XYZ
прошла успешно). Также обратите внимание, что test XYZ
эквивалентен [XYZ]
, т.е. приведенное выше также эквивалентно if [XYZ]; затем команда; fi
. Также обратите внимание, что любая точка с запятой ;
может быть заменена новой строкой, они эквивалентны.
Удалите условие test -e "$ file" &&
(оставьте только echo
) и посмотрите, что произойдет.
Удалите двойные кавычки вокруг "$ file"
и посмотрите, что произойдет, когда каталог, содержимое которого вы перечисляете, будет содержать имена файлов с пробелами. Добавьте set -x
в верхней части скрипта (или вызовите его как sh -x scriptname.sh
), чтобы включить вывод отладки и посмотреть, что происходит в деталях (для перенаправления отладки вывод в файл, запустите sh -x имя_сценария.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
)
}
Обратите внимание, что в некоторых реализациях оболочки существует жесткое ограничение (~ 4k) на количество символов, которое может быть передано в качестве аргумента для
(или любой встроенной или внешней команде, если на то пошло). Поскольку оболочка расширяется, встроенная, *
в список всех совпадающих имен файлов перед фактическим выполнением для
на нем, вы можете столкнуться с проблемой, если *
развернуть внутри каталога с большим количеством файлов (такая же проблема у вас) Я столкнусь при запуске, скажем, ls *
в том же каталоге, например, получите ошибку вроде Command too long
.)