Обновите Vim в RHEL до версии 7,4,2009

В версиях оболочки {} до версии 3.0.0 fish требовалось заключать в кавычки.

$ fish -c 'echo find -exec {} \;'
find -exec  ;

И в оболочке rc (также akanga, основанной на rc, но не es):

$ rc -c "echo find -exec {} ';'"
line 1: syntax error near '{'

Вероятно, это не те оболочки, которые имели в виду авторы документации GNU find, когда писали этот текст, поскольку fish была впервые выпущена в 2005 году (тогда как этот или похожий текст уже существовал в 1994 году), а rc изначально не была оболочкой Unix.

Ходят слухи, что некоторые версии csh (оболочка, в которой появилось расширение скобок) требовали этого. Но трудно отдать должное этим версиям, поскольку первый выпуск csh в 2BSD этого не делал. Вот как это проверялось в эмуляторе PDP11:

# echo find -exec {} \;
find -exec {} ;

А на странице man из 2BSD csh ясно сказано:

Как особый случай `{', `}' и `{}' передаются без помех.

Поэтому мне было бы очень странно, если бы последующая версия csh или tcsh позже нарушила это.

Это могло быть сделано для того, чтобы обойти некоторые ошибки в некоторых версиях. Все еще с этим 2BSD csh (то же самое в 2.79BSD, 2.8BSD, 2.11BSD):

# csh -x
# echo foo {} bar
echo foo {} bar
foo {} bar
# echo `echo foo {} bar`
echo `echo foo {} bar`
echo foo {} bar
foo  bar

Цитирование не помогает:

# echo `echo foo '{}' bar`
echo `echo foo '{}' bar`
echo foo {} bar
foo  bar

Вы могли бы процитировать всю подстановку команды:

# echo "`echo foo {} bar`"
echo `echo foo {} bar`
echo foo {} bar
foo {} bar

Но это передача одного аргумента внешнему echo.

В csh или tcsh вам нужно будет заключить в кавычки {}, если он не является самостоятельным, как в:

find . -name '*.txt' -type f -exec cp {} '{}.back' \;

(хотя такое использование find не является переносимым, так как некоторые findы расширяют {} только когда они сами по себе).

0
24.09.2018, 21:05
6 ответов

У Grep нет такой возможности.

Если вам не нужны имена файлов, почему вы используете опцию -nдля номеров строк?

Существует возможность вывести совпадающие имена файлов:

-l, --files-with-matches
Suppress normal output; instead print the name of each input file from which output would normally have been printed. The scanning will stop on the first match.

Вы можете использовать sedдля удаления последней части имени файла и сохранения только части каталога. Если вы хотите, чтобы каждый каталог был указан только один раз, вы можете запустить вывод через sort -u.

0
28.01.2020, 02:42

Основываясь на том, что сказал @RalfFriedl, если вам просто нужно имя каталога, в котором находится файл, соответствующий регулярному выражению, вы можете использовать имя каталога. Если в этом каталоге есть несколько файлов, вы можете свернуть свой вывод в отсортированный уникальный список, используя sort. Что-то вроде следующего:

dirname $(grep -Eril "RegEx" *) | sort -u
2
28.01.2020, 02:42

Рассмотрите возможность использования команды find.

find. -type d -regextype posix-extended -regex 'RegEx'

Ищет в текущем каталоге вещи типа каталог, соответствующие имена путей, используя posix -расширенный (так же, как grep -E ).

0
28.01.2020, 02:42

Используйте параметр grep -lи пост -обработайте его с помощьюsed:

grep -rilE "RegEx" * |sed 's![^/]*$!!' > outputfile.txt

Выражение sed удаляет (из $конца строки )любые символы, не являющиеся косой чертой, оставляя вам только имя каталога (s ).

0
28.01.2020, 02:42
# usage: dgrep regex dir
dgrep(){
        find "$2" -type d -print0 | xargs -0 sh -c '
                rex=$1; shift
                for d; do grep -sq "$rex" "$d"/* "$d"/.[!.]* && echo "$d"; done
        ' sh "$1"
}

В отличие от решений grep -rl+ sed, это будет сканировать только файлы в каталоге до тех пор, пока не будет найдено первое совпадение --, в зависимости от ваших данных, это может значительно ускорить работу.

Не стесняйтесь добавлять параметры grep после -sq; сценарий, в котором они будут передаваться в командной строке, возможен, но это все усложнит с небольшой пользой.

0
28.01.2020, 02:42

В системе GNU:

grep -ErliZ "RegEx". |
  LC_ALL=C sed -z 's|/[^/]*$||' |
  LC_ALL=C sort -zu |
  tr '\0' '\n'

Это не оптимально, поскольку поиск RegExпродолжается во всех файлах каталога даже после того, как там уже найдено совпадение.

Чтобы избежать этого и по-прежнему не запускать по одному grepдля каждого каталога, с помощью GNU awkвы можете сделать:

find. -type f -print0 | LC_ALL=C gawk -v RS='\0' '
  BEGIN{while ((getline < "/dev/stdin") > 0) ARGV[ARGC++] = $0}
  FNR == 1 {dir = FILENAME; sub("/[^/]*$", ""); if (dir in found) nextfile}
  /RegEx/ {found[dir]; print dir; nextfile}'

Мы используем LC_ALL=C, поэтому sub("/[^/]*$", "")может надежно удалить часть имени файла, но это означает, что декодирование текста в файлах не выполняется в соответствии с шарм-картой локали. Если вы знаете, что все пути к файлам являются допустимым текстом в текущей локали, вы можете удалить его. Или вы можете добавить -name '*'к find, чтобы пропускать имена файлов, которые содержат последовательности байтов, не образующие допустимые символы в локали.

0
28.01.2020, 02:42

Теги

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