Проблема сценария Linux Shell - PullRequest
5 голосов
/ 09 ноября 2010

У меня есть строка, разделенная точкой в ​​Linux Shell,

$example=This.is.My.String

Я хочу

1.Добавить строку перед последней точкой, например, я хочу добавить "Good.Long "до последней точки, поэтому я получаю:

This.is.My.Goood.Long.String

2.Получить деталь после последней точки, поэтому я получу

String

3. Поверните точкув подчеркивание, кроме последней точки, поэтому я получу

This_is_My.String

Если у вас есть время, пожалуйста, объясните немного, я все еще изучаю Регулярные выражения.

Большое спасибо!

Ответы [ 6 ]

10 голосов
/ 09 ноября 2010

Я не знаю, что вы подразумеваете под «Linux Shell», поэтому я буду считать bash. Это решение также будет работать в zsh, и так далее :

example=This.is.My.String
before_last_dot=${example%.*}
after_last_dot=${example##*.}
echo ${before_last_dot}.Goood.Long.${after_last_dot} 
This.is.My.Goood.Long.String

echo ${before_last_dot//./_}.${after_last_dot} 
This_is_My.String

Промежуточные переменные before_last_dot и after_last_dot должны объяснить мое использование операторов % и ##. //, я также думаю, что само собой разумеется, но я был бы рад уточнить, если у вас есть какие-либо вопросы.

Это не использует sed (или даже регулярные выражения), но подстановка встроенного параметра bash. Я предпочитаю придерживаться только одного языка на сценарий, используя как можно меньше вилок: -)

3 голосов
/ 10 ноября 2010

Вот версия, которая использует сопоставление регулярных выражений Bash (Bash 3.2 или выше).

[[ $example =~ ^(.*)\.(.*)$ ]]
echo ${BASH_REMATCH[1]//./_}.${BASH_REMATCH[2]}

Вот версия Bash, в которой используется IFS (Внутренний разделитель полей).

saveIFS=$IFS
IFS=.
array=($e)                    # *   split the string at each dot
lastword=${array[@]: -1}
unset "array[${#array}-1]"    # *
IFS=_
echo "${array[*]}.$lastword"  #     The asterisk as a subscript when inside quotes causes IFS (an underscore in this case) to be inserted between each element of the array
IFS=$saveIFS

* используйте declare -p array после этих шагов, чтобы увидеть, как выглядит массив.

3 голосов
/ 10 ноября 2010

Другие пользователи дали хорошие ответы для № 1 и № 2. У некоторых ответов на # 3 есть некоторые недостатки. В одном случае вы должны выполнить замену дважды. В другом случае, если у вашей строки есть другие подчеркивания, они могут быть забиты. Эта команда работает за один раз и влияет только на точки:

sed 's/\(.*\)\./\1\n./;h;s/[^\n]*\n//;x;s/\n.*//;s/\./_/g;G;s/\n//'
  1. Разбивает строку перед последней точкой, вставляя новую строку, и копирует результат в область удержания:

    s/\(.*\)\./\1\n./;h
    
  2. удаляет все до и включая новую строку из копии в пространстве образца и заменяет пространство пробела и пространство образца:

    s/[^\n]*\n//;x
    
  3. удаляет все после и включая новую строку из копии, которая сейчас находится в пространстве шаблона

    s/\n.*//
    
  4. заменяет все точки на подчеркивания в копии в пространстве образца и добавляет пробел в конец пространства образца

    s/\./_/g;G
    
  5. удаляет символ новой строки, добавляемый операцией добавления

    s/\n//
    

Затем сценарий sed завершается и выводится пространство шаблона.

В конце каждого пронумерованного шага (некоторые состоят из двух фактических шагов):

Шаг Шаблон Пространство Удержание Пространство

  1. This.is.My \n .String This.is.My \n .String

  2. This.is.My \n .String .String

  3. This.is.My .String

  4. This_is_My \n .String .String

  5. This_is_My.String .String

3 голосов
/ 09 ноября 2010

Решение

  1. Две версии этого тоже:
  2. Что вы хотите?
  3. sed 's/\([^.]*\)[.]\([^.]*[.]\)/\1_\2/g'

При использовании 3 вам, вероятно, потребуется выполнить замену (в целом) как минимум дважды (* полностью).

Пояснение

Запомните, в sed,запись \(...\) - это «захват», на который можно ссылаться как «\1» или аналогичный в тексте замены.

  1. Захватывать все до строки, начинающейся с точки, за которой следует точкапоследовательностью не точек (которые вы также захватываете);замените тем, что было до последней точки, новым материалом, последней точкой и тем, что было после нее.

  2. Игнорируйте все, вплоть до последней точки, с последующим захватом последовательностине являющиеся точки;заменить только на захват.

  3. Найти и захватить последовательность не точек, точку (не захваченную), за которой следует последовательность не точек и точки;замените первую точку подчеркиванием.Это делается глобально, но второй и последующие матчи не коснутся ничего подобного.Поэтому, я думаю, вам нужны проходы ceil (log 2 N), где N - количество заменяемых точек.Один проход имеет дело с 1 точкой для замены;два прохода сделки с 2 или 3;три прохода, сделки с 4-7 и т. д.

2 голосов
/ 09 ноября 2010

1.

$ echo 'This.is.my.string' | sed 's}[^\.][^\.]*$}Good Long.&}'
This.is.my.Good Long.string

до: точка, затем без точки до конца.после: очевидно, & это то, что соответствует первой части

2.

$ echo 'This.is.my.string' | sed 's}.*\.}}'
string

sed жадные совпадения, поэтому оно будет расширять первое замыкание (. *) как можно дальше, то есть допоследняя точка.

3.

$ echo 'This.is.my.string' | tr . _ | sed 's/_\([^_]*\)$/\.\1/'
This_is_my.string

преобразуйте все точки в _, затем поверните последнюю точку _ в точку.

(предостережение: это превратит «это».is.my.string_foo 'до' This_is_my_string.foo ', а не' This_is_my.string_foo ')

1 голос
/ 10 ноября 2010

Вам не нужны регулярные выражения вообще (эти сложные вещи болят мне глаза!), Если вы используете Awk и немного креативны.

1. echo $example| awk -v ins="Good.long" -F . '{OFS="."; $NF = ins"."$NF;print}'

Что это делает:
-v ins = "Good.long" говорит awk создать переменную с именем 'ins' с "Good.long" в качестве содержимого,
-F. говорит awk использовать точку в качестве разделителя для ваших полей для ввода,
-OFS говорит awk использовать точку в качестве разделителя для ваших полей в качестве вывода,
NF - количество полей, поэтому $ NF - последнее поле,
часть $ NF = ... заменяет последнее поле, добавляет текущую последнюю строку к тому, что вы хотите вставить (переменная с именем «ins», объявленная ранее).

2. echo $example| awk -F . '{print $NF}'

$ NF - последнее поле, вот и все!

3. echo $example| awk -F . '{OFS="_"; $(NF-1) = $(NF-1)"."$NF; NF=NF-1; print}'

Здесь мы должны проявлять креативность, так как Awk AFAIK не позволяет удалять поля. Конечно, мы устанавливаем разделитель поля вывода на подчеркивание.

$ (NF-1) = $ (NF-1) "." $ NF: сначала мы заменяем второе последнее поле последним, приклеенным ко второму последнему, с точкой между.
Затем мы дурачим awk, чтобы заставить его думать, что Количество полей равно количеству полей минус одно, следовательно, удаляя последнее поле!

Обратите внимание, что вы не можете сказать $ NF = "", потому что тогда будет отображаться два подчеркивания.

...