Подсчет всех файлов, соответствующих глобусу, в текущем каталоге и во всех его подкаталогах с использованием сценариев BASH

Да, так. Я сделал переустановку и перенастройку. Все было хорошо, пока я снова не начал добавлять субдомены. Возникла похожая проблема не слушания. На этот раз networkingвсе было в порядке.

Оказывается, символические ссылки на сайты с sites-availableна sites-enabledс помощью ln -sтребуют полных абсолютных путей. Я использовал относительные. Таким образом, ни один сайт не был включен. Хотя это и не решение проблемы с dhclient/ifup, оно решило проблему для меня.

0
10.05.2021, 16:54
2 ответа

Вы, кажется, неправильно -прочитали вопрос задания.

  1. он говорит "текущий каталог" , который является ., , а не~или~/linux2/q3

  2. он также говорит "и все подкаталоги" . Учитывая, что это, по-видимому, вводный курс по написанию сценариев оболочки -, крайне маловероятно, что они ожидают, что вы напишете свой собственный код в bash для рекурсии подкаталогов. То есть не задача для новичков.

    Это почти наверняка означает «использовать find, стандартный инструмент для рекурсивных подкаталогов».

  3. В нем сказано использовать glob, а не реализовывать собственное сопоставление имен файлов. Независимо от того, насколько хорошо вы пишете свой собственный код сопоставления с образцом, он НЕ использует подстановку.

    findимеет опцию -name, которая использует подстановочные знаки для сопоставления файлов.

    Обратите внимание, что здесь также не говорится «соответствует окончанию файла» или расширению файла. Он говорит «соответствует определенному шару» и дает «.txt» в качестве примера . Glob может сопоставлять расширения файлов, но его также можно использовать для сопоставления гораздо большего, чем просто это.

  4. «написать сценарий оболочки для выполнения X» (или подобные слова )не обязательно означают «написать сценарий оболочки, который не использует никаких внешних программ, используя только встроенные -команды». На самом деле это, конечно, не означает, что , если не указано явно.

    Вызов внешних программ для выполнения работы — это то, что делают сценарии оболочки,это совершенно нормально и ожидаемо для сценариев оболочки... особенно при использовании любой из стандартных утилит Unix, таких как findили wc.

    wc— это стандартная программа, которую можно использовать для подсчета количества символов, строк и/или слов в файле или стандартном вводе. В этом случае вам нужно только подсчитать количество строк, поэтому используйте опцию wc's -l.

#!/bin/bash

# Count the number of files matching a glob in the current directory
# and all subdirectories.
#
# The glob can be specified on the command line, in which case it
# MUST be quoted or escaped to prevent the shell from expanding it.
# e.g. use '*.txt' or \*.txt, not just *.txt.
#
# if the glob is not specified on the command line, the script prompts
# for a glob until one is provided.

myglob="$1"

while [ -z "$myglob" ] ; do
  read -p 'Enter a glob: ' myglob
done

numfiles=$(find. -type f -name "$myglob" | wc -l)
echo $numfiles

Если есть шанс, что любое из имен файлов в текущем каталоге имеет символы новой строки (, т.е. LFсимволы )в них (, что является допустимым символом в именах файлов unix ), затем используйте NULв качестве разделителя имен файлов вместо LF:

.
numfiles=$(find. -type f -name "$myglob" -print0 |
             awk -v RS='\0' '{count++}; END {print count}')

Вместо использования wc -lиспользуется сценарий awkдля подсчета имен файлов, разделенных NUL -.

Или, как указал Стефан Шазела в комментарии, вы можете сделать это только с помощью findиgrep:

numfiles=$(find.//. -type f -name "$myglob" | grep -c //)

Аргумент .//.начального каталога -приводит к тому, что findвыводит имена файлов с префиксом .//. Поскольку невозможно, чтобы //отображалось в имени файла из find, вы можете использовать grep -c //для подсчета файлов. .//появляется в имени файла только один раз, поэтому это работает независимо от того, есть ли в имени файла новые строки или нет.

Кстати, хорошей практикой программирования оболочки является всегда учитывать возможность новой строки и других проблемных символов (, например. пробелы, табуляции, точки с запятой -, амперсанды и т. д. )в именах файлов, даже если вы думаете, что это, вероятно, не будет проблемой. Это одна из причин, по которой вы всегда должны заключать в двойные -кавычки переменные при их использовании. И причина, по которой использование NUL в качестве разделителя имен файлов лучше, надежнее и безопаснее, чем просто использование LF.

Если вы объясните причину использования NUL в качестве разделителя вместо новой строки, это, вероятно, заслуживает дополнительных баллов.


Обновление

Даже если вам необходимо использовать два цикла for вместо find, вам все равно не следует выполнять собственное сопоставление с образцом. Ваш код не использует подстановочные знаки для сопоставления файлов -, он использует ваш собственный код сопоставления шаблонов. Это не то же самое, даже близко.

Вот пример использования двух циклов for, которые фактически используют подстановочные знаки для подсчета совпадающих файлов. Я добавил примечания под каждым циклом, чтобы объяснить их, но в скрипте вы просто запускаете один цикл за другим.

Цикл 1 для текущего каталога:

for f in $myglob; do
  [ -f "$f" ] && let numFile++
done

Этот forцикл является примером одного из очень немногих случаев, когда вы не хотите заключать в кавычки $myglobпри его использовании, потому что вы хотите оболочку чтобы расширить земной шар.

Почти во всех других случаях вы не хотите, чтобы оболочка расширяла переменные в командной строке, поэтому вы должны заключать их в двойные -кавычки :"$myglob", а не просто $myglob. Кроме того, хотя это и не относится к этому сценарию, вы все равно должны удваивать переменные массива -в кавычках, такие как "${array[@]}", даже если вы хотите, чтобы они были развернуты, потому что вы хотите, чтобы каждый отдельный элемент массива рассматривался как одно «слово».

В любом случае, это использует [ -f "$f" ]для проверки существования "$f" и является ли он обычным файлом, так что учитываются только файлы, а не каталоги (или что-то еще, например символические ссылки или именованные каналы, также известные как fifos ). Это делает то же самое, что и использование опции find's -type f.

Если вы хотите подсчитывать каталоги в ./вместо (или вместе с )файлами, вы должны использовать:

[ -d "$f" ] && let numDir++

Цикл 2 для непосредственных подкаталогов:

for f in */$myglob ; do
  [ -f "$f" ] && let numFile++
done

Это почти идентично первому циклу for, за исключением того, что он перебирает */$myglob, а не только $myglob.

Все вместе это что-то вроде:

#!/bin/bash
# comments deleted, same as version using find above.

myglob="$1"

while [ -z "$myglob" ] ; do
  read -p 'Enter a glob: ' myglob
done

for f in $myglob; do
  [ -f "$f" ] && let numFile++
done

for f in */$myglob ; do
  [ -f "$f" ] && let numFile++
done

echo "$(pwd)/ and $(pwd)/*/ combined contain $numFile files matching '$myglob'"

В отличие от версии find,эти циклы будут подсчитывать только файлы в текущем каталоге и каталогах непосредственно под ним. Он не будет рекурсивно углубляться в подкаталоги -и т. д.

Это, вероятно, то, что вы хотите, насколько я могу судить по вашему вопросу.

Вы можете ограничить глубину рекурсии в findс помощью опции -maxdepth. например. find. -maxdepth 2 -type f -name "$myglob".

2
28.07.2021, 11:33

Способ развернуть *.txtв текущем каталоге и подсчитать количество совпадающих имен состоит в том, чтобы

set --./*.txt

Это устанавливает позиционные параметры ($1, $2и т. д. )на имена, соответствующие шаблону подстановки. Если в оболочке bashустановлена ​​опция оболочки nullglob, это будет пустой список, если нет совпадений, иначе список будет содержать сам нерасширенный шаблон. Если параметр оболочки dotglobустановлен в оболочке bash, список также будет включать скрытые имена, если есть такие, которые соответствуют шаблону (*, в противном случае не соответствуют скрытым именам ).

Длина списка позиционных параметров составляет $#.

Это означает, что следующий короткий bashсценарий подсчитывает и сообщает, сколько (возможно скрытых )имен соответствует *.txtв текущем каталоге.

#!/bin/bash

shopt -s dotglob nullglob
set --./*.txt

printf 'There are %d names matching./*.txt here\n' "$#"

Если мы включим опцию оболочки globstar, мы получим доступ к **, который соответствует подкаталогам. Затем мы могли бы легко расширить наш скрипт выше для рекурсивного поиска в текущем каталоге и ниже:

#!/bin/bash

shopt -s dotglob nullglob globstar
set --./**/*.txt

printf 'There are %d names matching./**/*.txt here\n' "$#"

Очевидно, вы можете сохранить совпадающие имена в именованном массиве, если хотите.:

#!/bin/bash

shopt -s dotglob nullglob globstar
names=(./**/*.txt )

printf 'There are %d names matching./**/*.txt here\n' "${#names[@]}"

Если вы хотите вывести совпадающие имена в один столбец, вы можете сделать это с помощью

printf '%s\n' "$@"

или, если вы используете именованный массив в bash,

printf '%s\n' "${names[@]}"

Если вам нужно подсчитывать только обычные файлы, тогда вам, очевидно, нужно перебирать имена, совпадающие с глобусом:

#!/bin/bash

shopt -s nullglob dotglob globstar

regular_files=()

for pathname in./**/*.txt; do
    if [ -f "$pathname" ] && [ ! -L "$pathname" ]; then
        regular_files+=( "$pathname" )
    fi
done

printf 'There are %d regular files matching./**/*.txt\n' "${#regular_files[@]}"

Используемый выше тест -Lявляется истинным , если данный путь является символической ссылкой, поэтому используемая здесь комбинация тестов гарантирует, что мы учитываем только фактические обычные файлы, а не символическую ссылку на обычную ссылку. файл.

0
28.07.2021, 11:33

Теги

Похожие вопросы