У меня есть этот небольшой сценарий в sh
(Mac OSX 10.6) для просмотра массива файлов. Google прекратил быть полезным в этой точке:
files="*.jpg"
for f in $files
do
echo $f | grep -oEi '[0-9]+_([a-z]+)_[0-9a-z]*'
name=$?
echo $name
done
До сих пор (очевидно, Вам окружают гуру), $name
просто содержит 0, 1 или 2, в зависимости от если grep
найденный, что имя файла соответствовало обеспеченному вопросу. То, что я хотел бы, должно получить то, что в parens ([a-z]+)
и хранилище это к переменной.
Я хотел бы использовать grep
только, если это возможно. В противном случае никакой Python или Perl, и т.д. sed
или что-то как он – я плохо знаком с оболочкой и хотел бы напасть, это от *отклоняет пуристский угол.
Кроме того, как очень прикольная премия, мне любопытно относительно того, как я могу связать строку в оболочке? Группа, которую я получил, была строка "somename" сохраненный в $name, и я хотел добавить строку ".jpg" в конец его, мог я cat $name '.jpg'
?
Объясните, что продолжается, если у Вас есть время.
If you're using Bash, you don't even have to use grep
:
files="*.jpg"
regex="[0-9]+_([a-z]+)_[0-9a-z]*"
for f in $files # unquoted in order to allow the glob to expand
do
if [[ $f =~ $regex ]]
then
name="${BASH_REMATCH[1]}"
echo "${name}.jpg" # concatenate strings
name="${name}.jpg" # same thing stored in a variable
else
echo "$f doesn't match" >&2 # this could get noisy if there are a lot of non-matching files
fi
done
It's better to put the regex in a variable. Some patterns won't work if included literally.
This uses =~
which is Bash's regex match operator. The results of the match are saved to an array called $BASH_REMATCH
. The first capture group is stored in index 1, the second (if any) in index 2, etc. Index zero is the full match.
You should be aware that without anchors, this regex (and the one using grep
) will match any of the following examples and more, which may not be what you're looking for:
123_abc_d4e5
xyz123_abc_d4e5
123_abc_d4e5.xyz
xyz123_abc_d4e5.xyz
To eliminate the second and fourth examples, make your regex like this:
^[0-9]+_([a-z]+)_[0-9a-z]*
which says the string must start with one or more digits. The carat represents the beginning of the string. If you add a dollar sign at the end of the regex, like this:
^[0-9]+_([a-z]+)_[0-9a-z]*$
then the third example will also be eliminated since the dot is not among the characters in the regex and the dollar sign represents the end of the string. Note that the fourth example fails this match as well.
If you have GNU grep
(around 2.5 or later, I think, when the \K
operator was added):
name=$(echo "$f" | grep -Po '(?i)[0-9]+_\K[a-z]+(?=_[0-9a-z]*)').jpg
The \K
operator (variable-length look-behind) causes the preceding pattern to match, but doesn't include the match in the result. The fixed-length equivalent is (?<=)
- the pattern would be included before the closing parenthesis. You must use \K
if quantifiers may match strings of different lengths (e.g. +
, *
, {2,4}
).
The (?=)
operator matches fixed or variable-length patterns and is called "look-ahead". It also does not include the matched string in the result.
In order to make the match case-insensitive, the (?i)
operator is used. It affects the patterns that follow it so its position is significant.
The regex might need to be adjusted depending on whether there are other characters in the filename. You'll note that in this case, I show an example of concatenating a string at the same time that the substring is captured.
Невозможно только в grep, я полагаю
для sed:
name=`echo $f | sed -E 's/([0-9]+_([a-z]+)_[0-9a-z]*)|.*/\2/'`
Я попробую нанести удар по бонусу:
echo "$name.jpg"
если у вас есть bash, вы можете использовать расширенное подстановку
shopt -s extglob
shopt -s nullglob
shopt -s nocaseglob
for file in +([0-9])_+([a-z])_+([a-z0-9]).jpg
do
IFS="_"
set -- $file
echo "This is your captured output : $2"
done
или
ls +([0-9])_+([a-z])_+([a-z0-9]).jpg | while read file
do
IFS="_"
set -- $file
echo "This is your captured output : $2"
done
Это невозможно с чистым grep
, по крайней мере, не в целом.
Но если ваш шаблон подходит, вы можете использовать grep
несколько раз в конвейере, чтобы сначала привести строку к известному формату, а затем извлечь только нужный бит. (Хотя такие инструменты, как cut
и sed
, намного лучше справляются с этим).
Предположим, ради аргумента, что ваш шаблон был немного проще: [0-9 ] + _ ([az] +) _
Вы можете извлечь это так:
echo $name | grep -Ei '[0-9]+_[a-z]+_' | grep -oEi '[a-z]+'
Первый grep
удалит все строки, которые не соответствуют вашему общему шаблону, второй grep
(для которого указано - only-matching
) отобразит альфа-часть имени. Это работает только потому, что шаблон подходит: " echo $ name | grep {pattern} | вырезать -d _ -f 2
. Это заставляет вырезать
для разбора строки на поля путем разделения по разделителю _
и возвращает только поле 2 (номера полей начинаются с 1).
Философия Unix заключается в том, чтобы иметь инструменты, которые делают одно и делают это хорошо, и комбинируют их для решения нетривиальных задач, поэтому я бы сказал, что grep
+ sed
и т. д. - это более Unixy-способ решения вещи: -)
echo $ name | grep {pattern} | вырезать -d _ -f 2
. Это заставляет вырезать
для разбора строки на поля путем разделения по разделителю _
и возвращает только поле 2 (номера полей начинаются с 1).
Философия Unix заключается в том, чтобы иметь инструменты, которые делают одно и делают это хорошо, и комбинируют их для решения нетривиальных задач, поэтому я бы сказал, что grep
+ sed
и т. д. - это более Unixy-способ решения вещи: -)
Предложение для вас - вы можете использовать расширение параметра, чтобы удалить часть имени, начиная с последнего подчеркивания и далее, и аналогично в начале:
f=001_abc_0za.jpg
work=${f%_*}
name=${work#*_}
Затем имя
будет иметь значение abc
.
См. документацию разработчика Apple , выполните поиск вперед по запросу «Расширение параметров».