Замініть символ, окрім останніх x подій


9

У мене є файл, який має купу імен хостів, співвіднесених з IP-адресами, який виглядає приблизно так:

x-cluster-front-1 192.168.1.2
x-cluster-front-2 192.158.1.10
y-cluster-back-1 10.1.11.99
y-cluster-back-2 10.1.157.38
int.test.example.com 59.2.86.3
super.awesome.machine 123.234.15.6

Я хочу, щоб це виглядало так:

x-cluster-front-1 192.168.1.2
x-cluster-front-2 192.158.1.10
y-cluster-back-1 10.1.11.99
y-cluster-back-2 10.1.157.38
int-test-example-com 59.2.86.3
super-awesome-machine 123.234.15.6

Як я можу замінити. (крапки) з першого стовпця з - (дефіс), щоб полегшити сортування за другим стовпцем? Я думав використовувати sed для заміни крапок до першого пробілу, або замінювати кожну крапку, але останні три, але у мене виникають проблеми з розумінням регулярного вираження та sed. Я можу виконувати прості заміни, але це над головою!

Це частина більшого сценарію, який я писав у башті. Я застряг у цій частині.

Відповіді:


7

Ви можете використовувати AWK

awk '{gsub(/-/,".",$1);print}' infile

Пояснення

awkрозділяє лінію на пробіли за замовчуванням. Таким чином, перший стовпець рядка ( $1in awk-sese) буде тим, на якому ви хочете виконати заміни. Для цього ви можете використовувати:

 gsub(regex,replacement,string)

виконати необхідну заміну.

Зверніть увагу , що gsubпідтримується тільки gawkі , nawkале на багатьох сучасних дистрибутивах awkє м'якою посиланням на gawk.


1
+1 Побий мене до цього. Я думаю, що пояснення дійсно принесе користь і запитувачу, і майбутнім читачам.
Джозеф Р.

1
@JosephR. Вибачте, я не добре пояснюю, але я спробував і оновив ..
Рахул Патіль,

2
Специфікація POSIX awkзаснована на nawk, тому всі сучасні awkреалізації повинні мати gsub. На Solaris вам може знадобитися /usr/xpg4/bin/awkабо nawk.
Стефан Шазелас

@RahulPatil Якщо ви не заперечуєте, я додав кілька рядків, які, думаю, допоможуть іншим.
Йосиф Р.

@JosephR спасибі .., зараз це здається ідеальним .. :)
Рахул Патіл

6

Якщо вам потрібно зробити заміну в першому полі, найкраще скористатися дивним рішенням Рахуля, але будьте обережні, що це може вплинути на інтервал (поля переписані з єдиним пробілом між ними).

Ви можете уникнути цього, написавши натомість:

perl -pe 's|\S+|$&=~tr/./-/r|e' file

В -pозначає прапор «читати вхідний файл по рядках та роздрукувати кожен рядок після застосування сценарію дається -e». Потім замініть ( s|pattern|replacement|) першу послідовність символів, що не \S+містять пробілів ( ), відповідним малюнком ( $&) після заміни всіх .на -. Хитрість полягає в тому, щоб використовувати, s|||eде eоператор оцінить вираз як заміну. Отже, ви можете мати одну заміну ( tr/./-/), застосовану до матчу ( $&) попереднього ( s|||e).

Якщо вам потрібно замінити кожен, .за -винятком останніх 3 останніх, на GNU sedта припускаючи, що у вас є revкоманда:

rev file | sed 's/\./-/4g' | rev

1
Зауважте, що рішення Perl передбачає версію 5.14 або вище (для /rроботи).
Джозеф Р.

3

Sed - це не найпростіший інструмент для роботи - дивіться інші відповіді щодо кращих інструментів - але це можна зробити.

Для того, щоб замінити .на -тільки до першого місця, використання sв циклі.

sed -e '
  : a                     # Label "a" for the branching command
  s/^\([^ .]*\)\./\1-/    # If there is a "." before the first space, replace it by "-"
  t a                     # If the s command matched, branch to a
'

(Зверніть увагу, що деякі реалізації sed не підтримують коментарів у тому ж рядку. GNU sed.)

Щоб замість цього виконати заміну до останнього місця:

sed -e '
  : a                     # Label "a" for the branching command
  s/\.\(.* \)/-\1/        # If there is a "." before the last space, replace it by "-"
  t a                     # If the s command matched, branch to a
'

Ще одна методика використовує простір для утримування sed. Збережіть біт, який ви не хочете змінювати, у простір утримування, виконайте свою роботу, а потім пригадайте простір утримування. Тут я розділив лінію на останній пробіл і замінюю крапки тире в першій частині.

sed -e '
  h           # Save the current line to the hold space
  s/.* / /    # Remove everything up to the last space
  x           # Swap the work space with the hold space
  s/[^ ]*$//  # Remove everything after the last space
  y/./-/      # Replace all "." by "-"
  G           # Append the content of the hold to the work space
  s/\n//      # Remove the newline introduced by G
'

2

Оскільки Рахул дав вам канонічну відповідь на ваш випадок використання, я подумав, що я вирішу відповісти на титульну проблему: замінюючи всі, крім останніх x випадків регулярного вираження:

perl -pe '
    $count = tr{.}{.}; # Count '.' on the current line
    $x = 3;
    next LINE if $count <= $x;
    while(s{\.}{-}){   # Substitute one '.' with a '-'
        last if ++$i == $count - $x # Quit the loop before the last x substitutions
    }
$i = 0
' your_file

Вищевказаний код (перевірений) не передбачає, що у вас розділені пробіли поля. Він замінить усі крапки в рядку тире, за винятком останніх 3 крапок. Замініть 3код на свій смак.


2

Для цього можна використовувати безліч різних інструментів. Рахул Патіл уже подарував вам gawkодин, ось ось кілька інших:

  • перл

    perl -lane  '$F[0]=~s/\./-/g; print "@F"' file
    

    У -aперемикачі причина PERL автоматично розщеплені вхідні лінії на пробільних і зберегти отримані полів в масив @F. Перше поле, таким чином, буде $F[0]так ми замінимо ( s///) все входження .з -в першому полі , а потім надрукувати весь масив.

  • оболонки

     while read -r a b; do printf "%s %s\n" "${a//./-}" "$b"; done < file 
    

    Тут цикл while читає файл і автоматично розбивається на пробіли. Це створює два поля $firstта $rest. Конструкція ${first//pattern/replacement}замінює всі входження patternз replacement.


+1 Хоча я perlrun(1)скажу вам, що -aце "режим автоматичного роздвоєння", я вважаю за краще " awkрежим": D
Джозеф Р.

2

Я вважаю, що це читати трохи простіше, ніж великий бридкий вираз. В основному я просто розділив лінію на два поля в області пробілів і використовую sed у першій частині.

while read -r host ip; do
    echo "$(sed 's/\./-/g' <<< "$host") $ip"
done < input_file

Залежно від вашої оболонки, ви можете також використовувати $ {host //./-} замість команди sed.


0
sed 's/\./-/' <file name>

Без використання gв кінці команди ви можете це зробити ... Це просто замінить 1-шу появу шаблону

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.