#bash
#bash
Вопрос:
У меня есть школьное задание, в котором я должен написать скрипт, который найдет все файлы в указанном каталоге (и все его подкаталоги), которые содержат их имена внутри себя. Все это достаточно просто, но я столкнулся с проблемой. Есть файл, который называется просто '*'
. Я использую find
для поиска всех файлов в указанном каталоге, и когда он находит этот файл, он все перепутывает и печатает все файлы три раза. Итак, я подумал, что могу решить проблему, экранировав *
символ в find
выходных данных, но я не могу этого сделать. Допустим find
, выходные tests/*
данные, и мне нужно их изменить tests/*
. Вот что я пробовал:
sed 's/*/\*/g'
awk '{gsub("*", \*); print} '
Ни один из них не сработал. Вывод по-прежнему tests/*
. Я перепробовал все возможные комбинации обратной косой черты и звездочек, даже пытался как-то использовать одинарные кавычки, но я просто не могу заставить это работать. У кого-нибудь есть идеи, как это сделать?
РЕДАКТИРОВАТЬ: для дальнейшего уточнения, вот как выглядит моя ситуация. У меня есть каталог, tests
который содержит файлы '*'
, test
и z7.sh
. Я хочу найти все файлы в этом каталоге и сохранить их в массиве files
. Итак, я передал каталог в качестве аргумента скрипту, а затем сделал:
IFS=
Это выводит следующее: tests/* tests/test tests/z7.sh tests/test tests/z7.sh tests/test tests/z7.sh
это явно неправильно, мне нужно, чтобы вывод был tests/* tests/test tests/z7.sh
. Я думал, что решением будет экранировать звездочку, но я даже этого не могу сделать.
Комментарии:
1. Внедрение экранирующих символов и / или кавычек не работает в большинстве ситуаций, потому что оболочка анализирует кавычки и экранирует перед расширением переменных и заменой команд (а не после). См . BashFAQ # 20: Как я могу найти и безопасно обрабатывать имена файлов, содержащие символы новой строки, пробелы или и то, и другое? (те же методы защищают от подстановочных знаков). И заключите все замены переменных и команд в двойные кавычки, чтобы они не подвергались разделению слов или расширению подстановочных знаков.
2. @oguzismail Я буду использовать grep для каждого из элементов массива (то есть для каждого файла) и выясню, какой из файлов содержит его имя внутри себя. Поэтому, если файл с именем Mike содержит предложение "Привет, меня зовут Майк", grep найдет его. Эту часть я уже сделал, и она работает, мне просто нужно исправить звездочку, потому что из-за этого я не получаю правильный список файлов.
3. Это все равно не сработало, теперь вывод
tests/* tests/test tests/z7.sh tests/test tests/z7.sh
Ответ №1:
Это большая проблема при попытке поместить несколько элементов из выходных данных одной команды в массив: вы будете во власти разделения слов и расширения глобуса в bash.
Чтобы избежать этого, вы можете добавлять элементы один за другим в массив:
# IFS='' prevents splitting filenames with spaces
while IFS='' read -r line; do
files =("$line")
done < <(find "$1" -type f)
Или, с помощью bash 4.x , вы можете использовать mapfile/ readarray (они являются синонимами, но readarray может быть более выразительным):
mapfile -t files < <(find "$1" -type f)
Но если вам действительно нужно, вы можете изменить IFS bash и отключить расширение глобуса для этой команды:
# back the current IFS so we can restore it later
oldifs=$IFS
# don't split filenames with spaces
IFS=''
# disable glob expansion
set noglob
files=($(find "$1" -type f))
# restore previous state
IFS=$oldifs
set glob
У Shellcheck (инструмент статического анализа для Bash) есть действительно хорошая страница об этой проблеме: SC2207. Я бы рекомендовал иметь для этого линтер в вашем предпочтительном редакторе, чтобы помочь с таким неожиданным поведением.
Ответ №2:
Вам не нужно экранировать первую звездочку при вызове sed, поскольку одна звездочка является допустимым символом.
$ echo 'tests/*' | sed 's/*/\*/g'
tests/*
И в awk, если вы не заключаете регулярное выражение между косыми чертами, оно анализируется как строка, а неэкранированные обратные косые черты используются перед преобразованием в ERE.
$ echo 'tests/*' | awk '{gsub(/*/, "\*")} 1'
tests/*
Но я бы не стал беспокоиться об этом, поскольку есть лучшие способы выполнить описанную вами задачу. Например:
find "$1" -type f -exec sh -c '
for f; do
if grep -qF -- "${f##*/}" "$f"; then
printf '''%sn''' "$f"
fi
done' sh {}
n'
files=($(find "$1" -type f))
echo ${files[@]}
Это выводит следующее: tests/* tests/test tests/z7.sh tests/test tests/z7.sh tests/test tests/z7.sh
это явно неправильно, мне нужно, чтобы вывод был tests/* tests/test tests/z7.sh
. Я думал, что решением будет экранировать звездочку, но я даже этого не могу сделать.
Комментарии:
1. Внедрение экранирующих символов и / или кавычек не работает в большинстве ситуаций, потому что оболочка анализирует кавычки и экранирует перед расширением переменных и заменой команд (а не после). См . BashFAQ # 20: Как я могу найти и безопасно обрабатывать имена файлов, содержащие символы новой строки, пробелы или и то, и другое? (те же методы защищают от подстановочных знаков). И заключите все замены переменных и команд в двойные кавычки, чтобы они не подвергались разделению слов или расширению подстановочных знаков.
2. @oguzismail Я буду использовать grep для каждого из элементов массива (то есть для каждого файла) и выясню, какой из файлов содержит его имя внутри себя. Поэтому, если файл с именем Mike содержит предложение «Привет, меня зовут Майк», grep найдет его. Эту часть я уже сделал, и она работает, мне просто нужно исправить звездочку, потому что из-за этого я не получаю правильный список файлов.
3. Это все равно не сработало, теперь вывод
tests/* tests/test tests/z7.sh tests/test tests/z7.sh
Ответ №1:
Это большая проблема при попытке поместить несколько элементов из выходных данных одной команды в массив: вы будете во власти разделения слов и расширения глобуса в bash.
Чтобы избежать этого, вы можете добавлять элементы один за другим в массив:
Или, с помощью bash 4.x , вы можете использовать mapfile/ readarray (они являются синонимами, но readarray может быть более выразительным):
Но если вам действительно нужно, вы можете изменить IFS bash и отключить расширение глобуса для этой команды:
У Shellcheck (инструмент статического анализа для Bash) есть действительно хорошая страница об этой проблеме: SC2207. Я бы рекомендовал иметь для этого линтер в вашем предпочтительном редакторе, чтобы помочь с таким неожиданным поведением.
Ответ №2:
Вам не нужно экранировать первую звездочку при вызове sed, поскольку одна звездочка является допустимым символом.
И в awk, если вы не заключаете регулярное выражение между косыми чертами, оно анализируется как строка, а неэкранированные обратные косые черты используются перед преобразованием в ERE.
Но я бы не стал беспокоиться об этом, поскольку есть лучшие способы выполнить описанную вами задачу. Например: