Извлеките подстроку с помощью sed, которая останавливается при первом появлении конца

Я бы сделал так:

#! /bin/sh -
pattern=${1?Please provide a file name}
shift

[ "$#" -gt 0 ] || set /

find "$@" -iname "$pattern" | grep '^' && exit

echo >&2 "Not found"
exit 1

То есть, передайте name (обратите внимание, что find рассматривает его как (без учета регистра из-за -iname) шаблон для поиска имен файлов, а не как точное имя файла для поиска) в качестве первого аргумента. Все остальные аргументы - это каталоги или файлы для поиска, а если ни один из них не указан, то поиск будет производиться в /.

Вместо того чтобы хранить вывод в переменной и выводить его в конце, используйте grep в качестве сквозной функции, которая сообщает true, если файл найден.

Также я вывожу сообщение "Not found" на stderr вместо stdout и сообщаю о невозможности найти файл в статусе выхода.

Как отметил @ThomasN, существует обычная проблема, заключающаяся в том, что вы не можете надежно передавать имена файлов/каталогов в find. Если вы хотите искать в каталоге с именем -delete, вызов that-script name -delete, например, приведет к катастрофическим последствиям. В BSD find это можно обойти, сделав (перед вызовом find):

for i do
  set -- "$@" -f "$i"
  shift
done

Переносимо (хотя обратите внимание, что -iname не переносимо), вам нужно будет добавить . / к относительным путям, что может быть проблематично, например:

for i do
  case $i in
    (. | ./* | /*) ;; # /foo, ./foo are not a problem
    (*) i=./$i
  esac
  set -- "$@" "$i"
  shift
done
find "$@"...

Однако это влияет на вывод (например, вы увидите ./delete/name вместо -delete/name).

3
28.05.2017, 02:11
4 ответа

Подход grep(требуется поддержка PCRE):

s="This is a test some stuff I want string junk string end"
grep -Po 'te.*?ng' <<< $s

Альтернативный подходperl :

perl -ne 'print "$&\n" if /te.*?ng/' <<< $s

Вывод (для обоих подходов):

test some stuff I want string

  • .*? - ?вот нежадныймодификатор, указывает соответствие в минимальном моде
2
27.01.2020, 21:21

steeldriver правильно указал на нежадное совпадение с регулярным выражением SED (эмулировать perl's .*?), где John1024 ясно указывает:

регулярные выражения Sed соответствуют самому длинному совпадению. У Sed нет эквивалента нежадности.

Таким образом, есть два альтернативных способа обойти проблему. Во-первых, используйте то, что на самом деле имеет нежадное сопоставление, например perl:

$ str="This is a test some stuff I want string junk string end"
$ perl -pe 's/^.*(te.*?ng).*/\1/' <<<  "$str"                                                                            
test some stuff I want string

В качестве альтернативы вы можете дать sed больше контекста для группировки совпадений, т.е. добавить то, что будет следовать за первым "строковым" словом:

$ sed -r 's/^.*(te.*?ng)\ junk.*/\1/' <<<  "$str"                                                                        
test some stuff I want string
0
27.01.2020, 21:21

Сделайте это в два шага: сначала удалите префикс (в случае, если терминатор присутствовал в префиксе), затем удалите все после префикса. Используйте команду T, чтобы пропустить строку, если она не совпадает:

echo "This is a test some stuff I want string junk string end" |
sed -n 's/.*\(.te.*ng\)/\1/; T; s/\(ng\).*/\1/p'

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

echo "This is a test some stuff I want string junk string end" |
sed '/.*\(.te.*ng\)/!d; s/.*\(.te.*ng\)/\1/; s/\(ng\).*/\1/'

В качестве альтернативы выполните замену и окончательную печать только на совпадающих строках.

echo "This is a test some stuff I want string junk string end" |
sed '/.*\(.te.*ng\)/ { s/.*\(.te.*ng\)/\1/; s/\(ng\).*/\1/p; }'
1
27.01.2020, 21:21

Я бы предложил использовать команду cut в вашем случае

echo "I am a useful and I am a string. Did I mention that I'm a string?" | cut -d "string" -f1

Это разрезало бы строку на три части (перед первой, после 2. И между «строкой») с -d"" вы можете выбрать шаблон, который вы хотите использовать в качестве резака, а с -fNumber вы выбираете, какую часть взять. Проблема: "строка" будет удалена Решение:

String=`echo "I am a useful and I am a string. Did I mention that I'm a string?" | cut -d "string" -f1`
String="$(String) string"
echo $String

Он добавляет разделитель "string", который был удален, в конец переменной $String, которая была определена в выходных данных

0
27.01.2020, 21:21

Теги

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