Функция BASH не экранирует управляющие символы

Эй, ребята, у меня есть функция, которую я использую, чтобы найти вещи, но, к сожалению, каждый раз, когда я передаю ей управляющий символ ($intVal или же testing : и т.д.) это душит. Мне было интересно, что это было за исправление?

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

Во всяком случае, вот код.

function ffind()
{
     if [ $1 ] ; then
         find -type f | grep -ir '$1' * | grep -v '.svn'
     else
         echo "'$1' is not a valid resource"
     fi
}

Примеры):

$ ffind $intVal
'' is not a valid resource

$ ffind "testing :"
bash: [: testing: unary operator expected
'testing :' is not a valid resource

1 ответ

Решение

Первый пример

$ ffind $intVal
'' is not a valid resource

Это не работает, потому что $foo это синтаксис для переменных, и у вас нет переменной с именем intVal установить, так $intVal переводится в пустую строку. Поскольку переменная также не заключена в кавычки, аргументы вообще не передаются ffind,

Чтобы это исправить, избегайте $ - или \$intVal (обратная косая черта) или '$intVal' (В одинарных кавычках).

Если у вас на самом деле есть переменная с именем intVal впрочем, вместо этого заключите в двойные кавычки - "$intVal" - это расширит значение переменной, но не разделит ее.

Обратите внимание, что в bash нет такой вещи, как "передача по ссылке". Существует только передача по значению и (хитрая) передача по имени.

Второй пример

$ ffind "testing :"
bash: [: testing: unary operator expected
'testing :' is not a valid resource

Это не работает, потому что вы забыли поставить кавычки $1 в if [ $1 ] поэтому он может быть разделен по словам, и три аргумента передаются [ встроенная:

  • " [ "
  • " testing "
  • " : "
  • " ] "

вместо ожидаемых двух:

  • " [ "
  • " testing : "
  • " ] "

Пример № 1 также зависит от этого, так как [ $1 ] распадается на (" [ "," ] ") и не (" [ "," "," ] "). Пример № 1 работает случайно, так как, по-видимому, [ ] является действительным. (Я этого не знал...)

Чтобы решить эту проблему, поместите двойные кавычки $1 - [ "$1" ],

Примечание: пока [ стандартно, есть также специфичный для bash [[ оператор, который на самом деле имеет правила синтаксического анализа, отличные от остальной части кода - в частности, он не разделяет расширенные переменные. В Баш, [[ $1 ]] а также [[ "$1" ]] оба эквивалентны, в отличие от их [ альтернативы.

Больше ошибок

У вашей функции также есть несколько других проблем, которые не показаны в примерах.

find -type f | grep -ir '$1' * | grep -v '.svn'

В этой строке:

  1. Слово '$1' вокруг него одинарные кавычки. Это означает, что bash не будет расширять свое содержимое - вы фактически указываете grep искать регулярное выражение $1, а не для аргумента командной строки.

    Чтобы исправить это, используйте двойные кавычки вместо - "$1"

  2. Первой команде grep говорят рекурсивно искать содержимое всех файлов в текущем каталоге (-r и * подстановочные).

    В то же время вы передаете вывод find -type f в grep - похоже, пытается сказать grep искать имена всех файлов.

    Это не сработает, потому что grep, как и большинство фильтров, не будет читать из стандартного ввода, если ему будет предоставлен один или несколько файлов для поиска. Я не знаю, что вы пытаетесь найти - имена файлов или содержимое файлов - поэтому выберите один:

    • Чтобы искать только имена файлов, сохраните канал, но удалите спецификацию файла:

       find -type f | grep -i "$1" | ...
      
    • Для поиска только содержимого файла удалите find| вместо:

       grep -ir "$1" * | ...
      
    • Можно объединить оба, явно указав grep файл "stdin":

       find -type f | grep -i "$1" - * | ...
      
       find -type f | grep -i "$1" /dev/stdin * | ...
      

      (/dev/stdin работает со всеми программами Linux, в то время как - это соглашение, используемое некоторыми программами, включая grep.)

  3. Во второй команде grep регулярное выражение для поиска слишком широкое. (Помните, что это регулярное выражение, а не фиксированная строка.) .svn будет соответствовать даже что-то вроде not-a-svn-file ".

    Чтобы исключить .svn каталог, использовать grep -v "/\.svn/" вместо.

    При поиске содержимого файла (grep -ir ...), еще лучше избавиться от grep -v командовать целиком и добавить --exclude-dir=".svn" к первому.

Вы можете перестать читать на этом этапе

Приведенные ниже пункты являются хорошей практикой в ​​сценариях sh.

  1. function ключевое слово не нужно: ffind() { ... достаточно и работает во всех оболочках POSIX (пока function ffind не будет).

  2. В случае сбоя скрипта, программы или функции он должен вернуть статус "сбой" своему родителю. По соглашению, программы Unix считают 0 означать "успех" и все остальное "неудача" (хотя есть и исключения из последнего).

    Чтобы вернуть статус явно, используйте return <num> в функции (или exit <num> в автономном сценарии):

    else
        echo "'$1' is not a valid resource" >&2
        return 1
    fi
    
  3. Аналогично, сообщения об ошибках не следует смешивать с обычным stdout, а вместо этого записывать в stderr (fd # 2), используя >& оператор перенаправления (см. пример выше). Таким образом, вы можете перенаправить нормальный вывод в файл (например, ffind intVal > results.txt) пока на экране все еще отображаются ошибки.

Фиксированный код

ffind()
{
     if [ "$1" ] ; then
         grep -ir --exclude-dir=".svn" "$1" .
     else
         echo "'$1' is not a valid resource" >&2
         return 1
     fi
}

Лучшие инструменты

ack утверждает, что "лучше чем grep". Бег ack "testing :" будет искать ваш исходный код и автоматически пропустить .svn и подобные каталоги.

Другие вопросы по тегам