Команда Bash awk с кавычками

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 -индексированный (очень маленькие каталоги используют это кеш, если установлена ​​функция файловой системы)

8
14.09.2019, 15:38
7 ответов
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.)

7
27.01.2020, 20:08

Используйте опцию -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
6
27.01.2020, 20:08

Мне кажется, это идеальная возможность нанять вас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в одной и той же командной строке, согласно справочной странице, включенной в xfsprogs4.5.something.

1
27.01.2020, 20:08

Это ужасная уловка, но это быстро и легко...

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;       
1
27.01.2020, 20:08

С помощью 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 :пропустить

0
27.01.2020, 20:08

Чтобы запустить команду 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выше.

14
27.01.2020, 20:08

Это сделало это.

awk -F: -vID=$ID_minimum '$3>=1000 && $1!="nfsnobody" { print "xfs_quota -x -c '"'"'limit bsoft=5g bhard=6g ''"$1"'''"'"' /home "}' /etc/passwd
3
27.01.2020, 20:08

Теги

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