vmtouch
определенно НЕ справится с этим. vmtouch
выполняет open(2)
и mmap(2)
для файлов, которые вы указываете.
Даже если вы заставите ядро кэшировать readdir(2)
, это по-прежнему будет линейной операцией, и во многих случаях все равно попадет на диск:
In the current VFS readdir operation, the low-level file system is always called, even if the entire directory is in cache.
(07.08.2017 18 :40 UTC последующее наблюдение):
ls -l
делает гораздо больше, чем просто readdir
. readdir(2)
в любом случае устарел, readdir(3)
является оболочкой для (f)stat
в каталоге и getdents
в каталоге. Параметр -l
для ls
также вызывает lstat
для каждой записи в нем.
getdents
сам по себе является просто оболочкой для всего, что данная файловая система предоставляет для указателей .iterate_shared
и .iterate
на операции файловой структуры каталога. В случае ext4
это завершается ext4_readdir
.
Некоторые файловые системы реализуют кэширование на этом уровне. (NFS — очень наглядный пример ), но другие могут по-прежнему поражать диск.
В качестве корня вашего первоначального вопроса вы могли бы написать какой-нибудь демон, который постоянно продолжает работать getdents
в заданном каталоге, но иначе невозможно закрепить эти элементы dentry в кеше ядра, и возможно, что ядро полностью игнорирует, что кэш из отдельного потока (зависит от файловой системы ).
Обратим внимание на ext4
на мгновение: если файловая система имеет функцию dir_index
, вы можете выполнить chattr +I dir
для каталога, чтобы пометить каталог как htree -индексированный (очень маленькие каталоги используют это кеш, если установлена функция файловой системы)
for f in /etc/passwd;
Это немного глупо, так как на самом деле не существует цикла с одним значением.
Но проблема, похоже, заключается в печати одинарных кавычек из awk. Вы можете экранировать их в оболочке, но вы также можете использовать символы обратной косой черты -внутри awk для их вывода. \OOO
— это символ с числовым значением OOO(в восьмеричном ), поэтому \047
— это '
. Так что это был бы один из способов сделать это:
awk -F: -vID=$ID_minimum '$3>=1000 && $1!="nfsnobody" {
printf "xfs_quota -x -c \047limit bsoft=5g bhard=6g %s\047 /home\n", $1}' /etc/passwd
Вы можете использовать аналогичный экран в шестнадцатеричном коде, \x27
, но в некоторых реализациях он может быть неверно истолкован, если следующий символ является допустимой шестнадцатеричной цифрой. (И, конечно же, я предполагал ASCII или набор символов, совместимый с ASCII -, например. УТФ -8.)
Используйте опцию -f -
для awk, чтобы взять скрипт из стандартного ввода и здесь -документ:
awk -F: -v "ID=$ID_minimum" -f - <<'EOT' /etc/passwd
$3>=1000 && $1!="nfsnobody" {
print "xfs_quota -x -c 'limit bsoft=5g bhard=6g "$1"' /home "
}
EOT
Мне кажется, это идеальная возможность нанять васxargs
(или GNU Parallel):
getent passwd \
| awk -F: '$3>=1000 && $1!="nfsnobody" {print $1}' \
| xargs -I{} \
echo xfs_quota -x -c \"limit bsoft=5g bhard=6g {}\" /home
# output:
# xfs_quota -x -c "limit bsoft=5g bhard=6g userone" /home
# xfs_quota -x -c "limit bsoft=5g bhard=6g usertwo" /home
Преимущество использования xargs
или parallel
заключается в том, что вы можете просто удалить echo
, когда будете готовы выполнить команду по-настоящему (, возможно, заменив ее на sudo
, если необходимо ).
Вы также можете использовать опции -p
/--interactive
(этих утилит, последняя — GNU -только )или--dry-run
(parallel
только опции ), чтобы получить подтверждение перед запуском каждой из них или просто посмотреть что будет запускать до того, как вы его запустите.
Общий метод, использованный выше, должен работать на большинстве Unix-систем и не требует -особых xargs
опций GNU. Двойные кавычки do необходимо «экранировать», чтобы они отображались буквально в выводе. Обратите внимание, что «строка замены» {}
в xargs -I{}
может быть любой, какой вы предпочитаете, а -I
подразумевает-L1
(выполнение одной команды для каждой входной строки, а не группирование их ).
GNU Parallel не требует опции -I
({}
— это строка замены по умолчанию ), которая дает вам мгновенный бонус в виде параллельного выполнения многих заданий, даже если вы не хотите утруждать себя изучением любыеизегодругиеособенности .
В качестве примечания, я даже не уверен, что опция xfs_quota
's -c
должна использоваться вот так,хотя у меня нет файловых систем XFS для тестирования. У вас может даже не быть необходимого для работы со строкой в кавычках в первую очередь (, если только вы не ожидаете, что имена пользователей содержат пробелы, что, я думаю, возможно ), поскольку похоже, что вы можете указать несколько параметров -c
в одной и той же командной строке, согласно справочной странице, включенной в xfsprogs
4.5.something.
Это ужасная уловка, но это быстро и легко...
awk -F: -vQ="'" -vID=$ID_minimum '$3>=1000 && $1!="nfsnobody" { print "xfs_quota -x -c " Q "limit bsoft=5g bhard=6g $1" Q " /home "}' $f;
С помощью GNU Parallel вы можете:
getent passwd |
parallel --colsep : -q xfs_quota -x -c \
'limit bsoft=5g bhard=6g {=1 $_ eq "nfsnobody" and skip(); $arg[3] <= 1000 and skip(); =}' /home
Пояснение:
--colsep :
Разделить на:-q
не разбивать команду на пробелы (оставить '...' как одну строку){=1... =}
Оцените это выражение perl по первому аргументу строки $_ eq "nfsnobody" and skip();
Если значение==nfsnobody :, пропустить $arg[3] <= 1000 and skip();
Если аргумент3 <= 1000 :пропустить
Чтобы запустить команду xfs_quota -x -c 'limit bsoft=5g bhard=6g USER' /home
для каждого USER
, чей UID не ниже $ID_minimum
, рассмотрите возможность сначала проанализировать этих пользователей, а затем фактически запустить команду, а не пытаться создать строку представляющий команду, которую вы хотите запустить.
Если вы создаете командную строку, вам нужно eval
ее. Это неудобно и легко ошибиться. Лучше просто получить список имен пользователей, а затем запустить команду.
getent passwd |
awk -F: -v min="${ID_minimum:-1000}" '$3 >= min && $1 != "nfsnobody" { print $1 }' |
while IFS= read -r user; do
xfs_quota -x -c "limit bsoft=5g bhard=6g $user" /home
done
Обратите внимание, что на самом деле нет необходимости одинарных кавычек вокруг аргумента после -c
. Здесь я использую двойные кавычки, потому что я хочу, чтобы оболочка расширяла переменную $user
, которая содержит значения, извлеченные с помощью awk
.
Я использую ${ID_minimum:-1000}
при присвоении значения переменной min
в команде awk
. Это расширится до значения $ID_minimum
или до 1000
, если эта переменная пуста или не установлена.
Если бы вы действительно хотели этого, вы могли бы сделать так, чтобы приведенный выше цикл распечатывал команды вместо их выполнения:
getent passwd |
awk -F: -v min="${ID_minimum:-1000}" '$3 >= min && $1 != "nfsnobody" { print $1 }' |
while IFS= read -r user; do
printf 'xfs_quota -x -c "limit bsoft=5g bhard=6g %s" /home\n' "$user"
done
Обратите внимание еще раз, что использование двойных кавычек в выводимой командной строке (вместо одинарных кавычек )никоим образом не запутает оболочку, если вы будете выполнять сгенерированные команды, используя eval
или каким-либо другим способом. Если вас это беспокоит, просто поменяйте местами одинарные и двойные кавычки в первом аргументе на printf
выше.
Это сделало это.
awk -F: -vID=$ID_minimum '$3>=1000 && $1!="nfsnobody" { print "xfs_quota -x -c '"'"'limit bsoft=5g bhard=6g ''"$1"'''"'"' /home "}' /etc/passwd