Пусть Xargs использует псевдоним вместо двоичного

13

Bash 4.2 на CentOS 6.5:

По моему у ~/.bash_profileменя есть куча псевдонимов, в том числе:

alias grep='grep -n --color=always'

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

$ grep -Re 'regex_here' *.py

Однако, когда я запустил это недавно:

$ find . -name '*.py' | xargs grep -E 'regex_here'

результаты не были выделены, а номера строк не были напечатаны, что заставило меня вернуться назад и явно добавить -n --color=alwaysв grepкоманду.

  • Не xargsчитает псевдонимы в среде?
  • Если нет, есть ли способ заставить это сделать это?
MattDMo
источник
Этот Q & A имеет то, что вы хотите.
Псимон
@psimon верно, это по сути дела говорит о том, что я уже сделал в моем обходном пути - мне пришлось вручную расширять свой псевдоним в xargsкоманде. Я пытаюсь выяснить, есть ли способ, с помощью которого я могу напрямую назвать свой псевдоним xargs.
MattDMo
1
Вы пробовали export GREP_OPTIONS='-n --color=always'перед командой xargs?
doneal24
@ DougO'Neal спасибо, это сработало! Я добавлю это к моему .bash_profile. Не стесняйтесь написать ответ ...
MattDMo

Ответы:

10

Псевдоним является внутренним для оболочки, где он определен. Это не видно другим процессам. То же самое касается функций оболочки. xargsэто отдельное приложение, которое не является оболочкой, поэтому не имеет понятия псевдонимов или функций.

Вы можете заставить xargs вызывать оболочку вместо grepпрямой. Однако простого вызова оболочки недостаточно, вы также должны определить псевдоним в этой оболочке. Если псевдоним определен в вашем .bashrc, вы можете получить этот файл; однако это может не сработать, вы .bashrcвыполняете другие задачи, которые не имеют смысла в неинтерактивной оболочке.

find . -name '*.py' | xargs bash -c '. ~/.bashrc; grep -E regex_here "$@"' _

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

find . -name '*.py' | xargs bash -c '. ~/.bashrc; grep -E "$0" "$@"' regex_here

Вы можете явно выполнить поиск псевдонима. Тогда xargsувидим grep -n --color=always.

find . -name '*.py' | xargs "${BASH_ALIASES[grep]}" regex_here

В зш:

find . -name '*.py' | xargs $aliases[grep] regex_here

Кстати, обратите внимание, что find … | xargs … разрывы на имена файлов, содержащие пробелы (среди других) . Вы можете исправить это, перейдя к записям с нулевым разделением:

find . -name '*.py' -print0 | xargs -0 "${BASH_ALIASES[grep]}" regex_here

или с помощью -exec:

find . -name '*.py' -exec "${BASH_ALIASES[grep]}" regex_here {} +

Вместо вызова findвы можете делать все целиком внутри оболочки. Шаблон **/глобуса рекурсивно пересекает каталоги. В bash вам нужно shopt -s globstarсначала запустить этот шаблон глобуса.

grep regex_here **/*.py

Это имеет несколько ограничений:

  • Если много файлов совпадают (или если они имеют длинные пути), команда может завершиться ошибкой, поскольку она превышает максимальную длину командной строки.
  • В bash ≤4.2 (но не в более поздних версиях, ни в ksh или zsh) **/рекурсивные ссылки на каталоги.

Другой подход заключается в использовании процесса замены, как это предложено MariusMatutiae .

grep regex_here <(find . -name '*.py')

Это полезно, когда **/не применимо: для сложных findвыражений или в bash ≤4.2, когда вы не хотите использовать рекурсивные ссылки. Обратите внимание, что это нарушает имена файлов, содержащие пробелы; Обходной путь должен установить IFSи отключить глобализацию , но он начинает становиться немного сложным:

(IFS=$'\n'; set -f; grep regex_here <(find . -name '*.py') )
Жиль "ТАК - прекрати быть злым"
источник
спасибо за ясное объяснение того, почему псевдонимы не видны другим процессам
MattDMo
Можно также использовать процесс подстановки, см. Мой ответ.
MariusMatutiae
11

использование alias xargs='xargs '

alias: alias [-p] [name[=value] ... ]
(snip)
A trailing space in VALUE causes the next word to be checked for
alias substitution when the alias is expanded.
1,61803
источник
Спасибо за это, я не знал о трюке с космическим трейлером.
MattDMo
Np. Это также полезно с sudo
1.61803 21.11.15
2

Пожалуйста, примите это как демонстрацию другого подхода, который я не могу найти в соответствующем вопросе SO :

Вы можете написать функцию-обертку, для xargsкоторой проверяется, является ли первый аргумент псевдонимом, и если это так, разверните ее соответствующим образом.

Вот код, который делает именно это, но, к сожалению, требует оболочки Z и, следовательно, не запускает 1: 1 с bash (и, честно говоря, я не привык к bash достаточно для его портирования):

xargs () {
        local expandalias
        if [[ $(which $1) =~ "alias" ]]; then
                expandalias=$(builtin alias $1) 
                expandalias="${${(s.'.)expandalias}[2]}"
        else
                expandalias=$1
        fi
        command xargs ${(z)expandalias} "${(z)@[2,-1]}"
}

Доказательство того, что это работает:

zsh% alias grep = "grep -n" ´                           # включает номер строки соответствия
zsh% find foo -name "* .p *" | xargs grep -E тест
Foo / bar.p0: 151: # Данные = тест
foo / bar.p1: 122: # data = test # включены номера строк
zsh% unalias grep 
zsh% find foo -name "* .p *" | xargs grep -E тест
Foo / bar.p0: # данные = тест
foo / bar.p1: # data = test # номера строк не включены
ЗШ% 
МРУ
источник
1

Более простое и элегантное решение - использовать процесс подстановки :

grep -E 'regex_here' <( find . -name '*.py')

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

Просто будьте осторожны, не оставляйте пробела между перенаправлением и круглыми скобками, иначе bash выдаст ошибку. Насколько я знаю, процесс замены поддерживается Bash, Zsh, Ksh {88,93}, но не pdksh (мне сказали, что это должно быть еще не ).

MariusMatutiae
источник
Я думаю, что разработка pdksh мертва. Mksh является более или менее преемником проекта - «получается довольно сложно (концепция разбора делается в голове у tg @)» .
Жиль "ТАК - перестань быть злым"
Подстановка процессов - это хороший метод для сложных findкоманд, хотя вам нужно помнить, что он будет разбит на пробелы, и это не может быть исправлено так легко, как find | xargsможет быть (путем переключения на -print0и -0или с помощью -exec). Когда это применимо, **/это проще и надежнее.
Жиль "ТАК - перестань быть злым"
0

grep будет читать набор параметров по умолчанию из переменной среды GREP_OPTIONS. Если вы положите

 export GREP_OPTIONS='--line-number --color=always'

в вашем .bashrc переменная будет передана в подоболочки, и вы получите ожидаемые результаты.

doneal24
источник
Но не ставьте --line-numberили --color=alwaysв , GREP_OPTIONSесли это не только для одной команды, это нарушит много сценариев. --color=autoэто нормально иметь там, и это все. Поместив эту строку в ваш, .bashrcвы сломаете много вещей.
Жиль "ТАК - перестань быть злым"
@Gilles Установка псевдонимов или переопределение параметров по умолчанию для любой команды для учетной записи root - это плохо. Установка этих параметров для учетной записи пользователя вряд ли вызовет много проблем. Я не могу придумать какие-либо пользовательские сценарии, которые являются проблематичными.
doneal24
Почти любой сценарий, который использует grep способом, выходящим за рамки тестирования, присутствие вхождения будет прервано. Например, из /etc/init.d/cronмоей системы: value=`egrep "^${var}=" "$ENV_FILE" | tail -n1 | cut -d= -f2` . Или из /usr/bin/pdfjam: pdftitl=`printf "%s" "$PDFinfo" | grep -e … | sed -e …` . Псевдоним не является проблемой, так как он не виден в сценариях.
Жиль "ТАК - перестань быть злым"
@ Жиль, мне известно о многих подобных сценариях. Те, которые я не могу обойти, обычно запускаются только пользователем root (например, /etc/init.d/cron). Лично у меня нет никаких псевдонимов , определенных на моих учетных записях и я не установить параметры в файле гс или через переменные окружения , чтобы изменить поведение по умолчанию команд. Я предпочитаю предсказуемость, а не удобство.
doneal24
Псевдонимы не нарушают предсказуемость, так как они не видны скриптами. Установка GREP_OPTIONSочень сильно нарушает предсказуемость, за исключением нескольких опций типа --color=auto(для которых она и была разработана).
Жиль "ТАК - перестань быть злым"