Почему ls. * Показывает файлы, отличные от ls ./.*?

Давайте посмотрим на конвейер:

/sbin/iptables-save -t filter | 
grep -- "-A INPUT" | 
grep -v "fail2ban-\|f2b-" | 
sed -e "s#^-A#apply_rule /sbin/iptables -D#g" | 
xargs -0 echo -e "`declare -f apply_rule`\n" | 
/bin/bash

iptables генерирует список правил, по одному правилу на строку. Другими словами, каждое правило отделяется символом новой строки, \ n . С показанными параметрами команды grep и sed обрабатывают свой ввод по одной строке за раз. Другими словами, они также ожидают ввода, разделенного новой строкой, и производят вывод, разделенный новой строкой. xargs -0 , однако, ожидает ввода, разделенного нулями. Поскольку выходные данные предшествующих команд не содержат нулевых символов, xargs пытается прочитать весь свой стандартный ввод сразу как единый элемент. Вот почему он генерирует сообщение об ошибке «слишком длинная строка аргумента».

Решение состоит в том, чтобы указать xargs ожидать ввода, разделенного новой строкой. Для этого мы добавляем опцию -d '\ n' . Однако мы также хотим, чтобы он обрабатывал только одну строку за раз. Для этого нам нужно указать -n1 . Собираем все вместе:

/sbin/iptables-save -t filter | 
grep -- "-A INPUT" | 
grep -v "fail2ban-\|f2b-" | 
sed -e "s#^-A#apply_rule /sbin/iptables -D#g" | 
xargs -n1 -d '\n' echo -e "`declare -f apply_rule`\n" | 
/bin/bash

Документация

Из man xargs :

-d delim
Элементы ввода завершаются указанным символом. Кавычки и обратная косая черта не являются особенными; каждый символ во входных данных воспринимается буквально. Отключает строку конца файла, которая обрабатывается как любой другой аргумент. Это можно использовать, когда входные данные состоят из элементов, просто разделенных новой строкой, хотя почти всегда лучше разрабатывать свою программу для использования --null там, где это возможно. Указанный разделитель может быть одиночным символом, escape-кодом в стиле C, например \ n, или восьмеричным или шестнадцатеричным escape-кодом. Восьмеричные и шестнадцатеричные escape-коды понимаются как для команды printf. Многобайтовые символы не поддерживаются.

-n max-args
Используйте не более max-args аргументов для каждой строки команды . Если размер превышен (см. Параметр -s), будет использоваться меньше аргументов, чем max-args, если не задана опция -x, и в этом случае xargs завершится.

3
20.04.2016, 03:01
2 ответа

Параметр $ GLOBIGNORE обрабатывается оболочкой, когда он расширяет подстановочный знак в вашей командной строке. В первом случае оболочка сначала расширяет . * до .ignoramus , что соответствует $ GLOBIGNORE , поэтому он не включается в имена, передаваемые в LS .

Во втором случае оболочка расширяет ./.* до ./. Ignoramus , что не соответствует $ GLOBIGNORE . Если вы установите для $ GLOBIGNORE значение .ignoramus: ./. Ignoramus , то поведение вашей второй команды будет соответствовать вашей первой.

4
27.01.2020, 21:16

Краткая причина: строка ./. Ignoramus не соответствует .ignoramus .

Краткое решение: используйте GLOBIGNORE = * ignoramus (также прочтите подробности в конце).

$ GLOBIGNORE=*ignoramus
$ ls .* | grep ignor                 ### empty
$ ls ./.* | grep ignor               ### also empty

Звездочка соответствует чему угодно (даже косая черта / перед именем).
Конечно, это также будет соответствовать foobar.ignoramus . О некоторых обходных путях читайте в конце.


Расширенный ответ:

То же самое происходит с обычным файлом:

$ mkdir /tmp/mytest; cd /tmp/mytest
$ touch f1 f2 filetotest
$ GLOBIGNORE=filetotest
$ ls -1
f1
f2
filetotest

$ ls -1 f*
f1
f2

$ ls -1 *
f1
f2

$ ls -1 ./f*
./f1
./f2
./filetotest

GLOBIGNORE не будет соответствовать ./ filetotest , но если мы используем:

$ GLOBIGNORE="*filetotest"
$ ls -1 ./f*
./f1
./f2

$ ls -1 ./*
./f1
./f2

$ ls -1
f1
f2
filetotest

Шаблон в GLOBIGNORE будет соответствовать ] (что-то) filetotest .

Для вашего примера:

$ touch .ignoramus
$ GLOBIGNORE='*ignoramus'
ls -1a
.
..
f1
f2
filetotest
.ignoramus

$ ls .* | grep ignor
$ ls ./.* | grep ignor

Даже с некоторым путем перед файлом это работает:

$ GLOBIGNORE='*ignoramus:*filetotest'
$ mkdir -p long/dir/to/test
$ touch long/dir/to/test/{f3,f4,.ignoramus,.other,filetotest}

$ ls -1 long/dir/to/test/f*
long/dir/to/test/f3
long/dir/to/test/f4

$ ls -1 long/dir/to/test/.[^.]*
long/dir/to/test/.other

$ shopt -s dotglob
$ ls -1 long/dir/to/test/*
long/dir/to/test/f3
long/dir/to/test/f4
long/dir/to/test/.other

$ unset GLOBIGNORE; shopt -s dotglob
$ ls -1 long/dir/to/test/*
long/dir/to/test/f3
long/dir/to/test/f4
long/dir/to/test/filetotest
long/dir/to/test/.ignoramus
long/dir/to/test/.other

Конечно, это также будет соответствовать bar.ignoramus .Если это нежелательно, используйте:

$ GLOBIGNORE=.ignoramus:./.ignoramus

Или, если вы хотите, чтобы был включен какой-либо путь:

$ GLOBIGNORE=.ignoramus:*/.ignoramus
1
27.01.2020, 21:16

Теги

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