Я работаю над написанием bash-скрипта или команды, которая будет:
X
из его аргументов, которые являются каталогами, содержащими не более 20 файлов. X
, должно быть набрано с клавиатуры. X
должен содержать названия каталогов и их разрешения. Я начал с поиска каталогов, содержащих не более 20 файлов, таким образом:
find . -maxdepth 1 -type d -exec bash -c "echo -ne '{} '; ls '{}' | wc -l" \; | awk '$NF<=20'
Но у меня возникают проблемы с сохранением аргументов в конкретном файле.
Вы сказали наличие затруднений при сохранении в конкретный файл, имя которого обеспечивается пользователем сценария, и Вы подчеркнули желание справки с этим конкретно. Но Вы не упоминали проблемы, предлагающей пользователю ввести имя файла. Таким образом, я предполагаю, что у Вас есть та работа, и что Вы делаете что-то вроде IFS= read -r outfile
, где outfile
название переменной, которая будет содержать обеспеченное пользователями выходное имя файла.
Если вывод записи в файл, имя которого сдержано outfile
все, что Вы оставили, чтобы сделать, затем Вы уже быть очень близко к Вашей цели. Замена outfile
с любым именем переменной Вы выбрали...
$outfile
, и Вы можете и должны предотвратить нежелательное разделение и globbing путем включения этого в двойные кавычки, т.е. путем записи "$outfile"
.>out.txt
записать в файл, имя которого буквально out.txt
, можно записать в файл, имя которого сдержано Ваш outfile
переменная путем записи >"$outfile"
.Это могло бы быть всем, в чем Вы нуждаетесь. Но существуют некоторые другие модификации, которые Вы могли бы рассмотреть.
find
и сделайте большую часть того, без чего Вы нуждаетесь -exec
.Как steeldriver говорит от Ваших установленных целей, похоже, что Вы не хотите, чтобы Ваш сценарий рассмотрел все файлы в текущем каталоге, как find . -maxdepth 1 ...
делает, но вместо этого только полагайте, что аргументы передали Вашему сценарию.
Однако, может быть разумно использовать find
для этого и одного преимущества выполнения так то, что, если действительно необходимо изменить его для работы рекурсивно позже, который будет очень легок. (С другой стороны, часто неблагоразумно предпринять дополнительное усилие подготовиться к опциям, которые Вы, возможно, должны были бы добавить позже, так как трудно предсказать, каково функции это на самом деле будет.)
В остальной части этого ответа я исследую ту возможность. Я дам то, что могло бы быть полным решением, но Вы можете или не можете решить, что это приемлемо для Ваших потребностей. Вы, вероятно, захотите изменить его в некотором роде. Моя цель не состоит в том, чтобы предоставить полируемый сценарий, но вместо этого продемонстрировать методы, которые можно использовать. (Вы, вероятно, получите другие ответы, которые демонстрируют подходы как циклы оболочки и проверяющий каждый файл с [ -d filename ]
как steeldriver предложенный.)
Если Вы хотите использовать find
, можно передать аргументы сценарий, полученный к find
как поддерживает его поиск вместо того, где Вы в настоящее время писали .
, и используйте -maxdepth 0
таким образом, это на самом деле не убывает в них. Это сделало бы неправильную вещь, если некоторые аргументы имени файла Вашему сценарию запускаются с -
, так как они могли быть интерпретированы особенно find
(как опции, предикаты или действия). Но большинство команд рассматривает споры с продвижением -
символы особенно; обычно достаточно зарегистрировать это поведение для других пользователей и гарантировать, чтобы Вы случайно не передавали такие пути к своему сценарию. (Если Вы действительно хотите считать запись в текущем каталоге названной как -foo
, можно передать его имя как ./-foo
вместо этого.)
Для простоты я сначала рассмотрю, как решить более легкую проблему вывода строк для всех аргументов, переданных сценарию, которые являются названиями каталогов, неважно, сколько записей они содержат (т.е. игнорирование "не больше, чем 20 файлов" требование).
find "$@" -maxdepth 0 -type d -printf '%M %p\n' 2>/dev/null >"$outfile"
Вот то, как это работает:
"$@"
расширяется до списка позиционных параметров всего сценария. (Параметры командной строки, переданные Вашему сценарию, становятся значениями его позиционных параметров $1
, $2
, $3
, и т.д.) Каждый будет передан find
как отдельный аргумент. Это работает, даже если некоторые имена содержат пробелы.-maxdepth 0
говорит find
для не взгляда немного глубже, чем пути, Вы явно передали его. Поэтому это будет только полагать, что параметры командной строки передали Вашему сценарию.-type d
находит только каталоги, когда Вы использовали его в своей команде.-printf '%M %p\n'
печать ls
- разработайте символьную строку полномочий (%M
), пространство, имя файла (%p
), и концы строка (\n
). См. документацию -printf
действие в man find
.find
с 2>/dev/null
. Это перенаправляет стандартную погрешность (дескриптор файла 2) к /dev/null
(специальное "устройство", которое выбрасывает его вход). Если Вы хотите видеть сообщения об ошибках, когда аргументы передали Вашему сценарию, не называют ничего вообще, просто опускают эту часть. (Сценарий все еще сделает свое задание, даже при том, что сообщения об ошибках отображены. Таким образом, они - эффективно предупреждения, а не ошибки.), Если Вы хотите некоторое другое поведение, можно работать над этим и/или дать больше информации о требованиях. Я называю это "быстрым и грязным" подходом, потому что это возможно для find
для издания других сообщений об ошибках и предупреждающих сообщений, и они также были бы подавлены.>"$outfile"
записи к выходному файлу, имя которого сдержано outfile
переменная, как описано выше.Одна вещь рассмотреть - то, что происходит, если пользователь не передает аргументов. Затем find
не видит корней для запуска с. Когда это происходит, это ведет себя, как будто Вы передали .
. (Не все find
реализации рассматривают отсутствие корней как подразумеваемое .
, но GNU находит, реализация находки в Ubuntu, делает.) Необходимо обработать это..., если Вы не хотите того поведения.
Разумно использовать -exec
для вызова другой команды, которая будет считать файлы в каждом каталоге, и подход к этому на правильном пути. Но это должно пойти перед -printf
действие (или безотносительно другого действия Вы используете для причины find
производить имена файлов), так, чтобы это действие только произошло для каталогов, которые проходят тест.
Если Вы принимаете решение считать файлы путем подсчета сколько строк вывода ls
команда производит, затем это ls
команда должна быть вызвана с -q
опция. Это вызвано тем, что имена файлов могут содержать странные символы, которые Вы не могли бы ожидать, включая символы новой строки. -q
заменяет такие символы ?
s, который во многих целях был бы несоответствующим, но так как Вы на самом деле не смотрите на имена, это прекрасно. (Можно также хотеть использовать -1
опция, потому что, хотя это избыточно когда ls
выводы к каналу (как здесь), это ясно документирует Ваше намерение это ls
испустите одну строку на файл.) В целом необходимо постараться не анализировать ls
, но так как Вы не заботитесь о фактических именах файлов и -q
гарантирует, чтобы Вы получили одну строку на файл, это хорошо.
Но я покажу другую технику вместо этого. Можно поместить этот тест после -type d
но прежде -printf
:
-exec bash -c 'a=("$1"/*); ((${#a[@]} <= 20))' _ {} \;
bash -c ...
использование a bash
оболочка. Я записал bash
вместо sh
потому что существует a bash
функция я хочу использовать: массивы. Выполнения оболочки a=("$1"/*); ((${#a[@]} <= 20))
. В первой команде, a=("$1"/*)
:
"$1"
расширяется до первого позиционного параметра, который соответствует имени файла find
передачи вместо {}
. Это - название обрабатываемого каталога."$1"/*
расширяется до путей файлов, содержавшихся в том каталоге, опуская тех, имена которых запускаются с .
, как ls
команда сделала. (Если Вы хотите считать их также, можно записать shopt -s dotglob;
прежде a=...
.)Массив называют a
и его размер может позже быть получен путем записи ${#a[@]}
. Вторая команда, ((${#a[@]} <= 20))
, использует в своих интересах это:
((
))
оценивает арифметические выражения и возвращается верный / успех, если они являются ненулевыми, ложь/отказ, если они - нуль.${#a[@]}
расширяется до размера массива, который является количеством записей в каталоге find
переданный вместо {}
. Это - то, что необходимо сравнить с 20.<= 20
сравнивает его с 20.Можно попытаться экспериментировать с другими значениями, чем 20, чтобы гарантировать, что это работает правильно.
Полный сценарий мог выглядеть примерно так (но помните, я не знаю всех Ваших требований, и это предназначается намного больше как демонстрация, чем как решение):
#!/bin/sh
if [ "$#" -eq 0 ]; then
printf '%s: warning: no arguments, behaving as "%s ."\n' "$0" "$0"
fi
printf 'Output filename> '
IFS= read -r outfile
find "$@" -maxdepth 0 -type d \
-exec bash -c 'a=("$1"/*); ((${#a[@]} <= 20))' _ {} \; \
-printf '%M %p\n' 2>/dev/null >"$outfile"
Несколько соображений:
.
.find
-exec
действие полагается bash
для массива, больший сценарий, который читает ввод данных пользователем и вызовы find
не использует никакие подобные функции. Можно выполнить его с bash
если Вам нравится, и можно изменить hashbang на #!/bin/bash
. Но я пошел голова и записал это как #!/bin/sh
, поскольку это должно быть достаточно.Я назвал свой файл сценария lt20
. Можно назвать его вообще, Вам нравится, но необходимо сделать это исполняемым файлом путем выполнения chmod +x lt20
(но с любым именем Вы действительно давали его). Вот то, на что это похоже для использования сценария с текстом, введенным пользователем, показанным курсивом:
$ ./lt20 /usr/*
Output filename> out
$ cat out
drwxr-xr-x /usr/arm-linux-gnueabi
drwxr-xr-x /usr/arm-linux-gnueabihf
drwxr-xr-x /usr/games
drwxr-xr-x /usr/lib32
drwxr-xr-x /usr/libexec
drwxr-xr-x /usr/local
drwxr-xr-x /usr/src
Моя машина имеет двенадцать подкаталогов /usr
, и они были все переданы как аргументы сценарию, но только те, которые имеют 20 или меньше записей (другой, что записи, которые запустились с .
) были перечислены. У всех них, оказалось, были те же полномочия, drwxr-xr-x
, но сценарий только показывает ту же строку разрешения для каждой записи, когда у них на самом деле есть те же полномочия.