Видаліть останній символ рядка, використовуючи маніпуляцію з рядком у скрипті оболонки


187

Я хотів би видалити останній символ рядка, я спробував цей маленький сценарій:

#! /bin/sh 

t="lkj"
t=${t:-2}
echo $t

але він друкує "lkj", що я роблю неправильно?

Відповіді:


115

У оболонці POSIX синтаксис ${t:-2}означає щось інше - він розширюється до значення, tякщо tвстановлено і ненулеве значення, інакше до значення 2. Щоб обрізати один символ за допомогою розширення параметра, потрібен синтаксис, який, мабуть, є${t%?}

Зверніть увагу , що в ksh93, bashабо zsh, ${t:(-2)}або ${t: -2}(зверніть увагу на пробіл) є законними як розширення підрядка , але, ймовірно , не те , що ви хочете, так як вони повертають підрядка , починаючи з позицією 2 символи в с кінця (тобто видаляє перший символ iз рядок ijk).

Докладнішу інформацію див. У розділі Розширення параметрів оболонки в Довідковому посібнику Bash:


4
Чи хотіли б ви пояснити, у чому полягає магія "%?" ?
afraisse

8
@afraisse ${parameter%word}видаляє найкоротший суфічний узор word- див. розділ Розширення параметрівman bash
steeldriver

3
Це добре працює для Bash 4.1.2: $ {t%?} Для людей, які застрягли з CentOS / RHEL 6.x
Joey T

185

З bash4.2 і вище, ви можете:

${var::-1}

Приклад:

$ a=123
$ echo "${a::-1}"
12

Зауважте, що для старих bash(наприклад, bash 3.2.5для ОС X) слід залишати пробіли між колонами та після них:

${var: : -1}

13
Це працює для bashверсії 4.2-альфа і вище, занадто погана версія, до якої я маю доступ. : - /
hjk

2
@iamaziz: З журналу зміни змін, негативна довжина в ${var:offset:lenght}додана лише в bash 4.2. Можливо, OSX додає свій власний патч для bash.
cuonglm

1
@cuonglm не працює: /
iamaziz

1
Не працює на Mac.
shinzou

1
MACsters, погляньте на відповідь Русса
П я

67

для видалення останніх nсимволів з рядка, в якому sedАБО не використовується awk:

> echo lkj | rev | cut -c (n+1)- | rev

тому, наприклад, можна видалити останній символ one characterза допомогою цього:

> echo lkj | rev | cut -c 2- | rev

> lk

з revmanpage:

ОПИС
Утиліта rev копіює вказані файли на стандартний вихід, змінюючи порядок символів у кожному рядку. Якщо жодні файли не вказані, зчитується стандартний вхід.

ОНОВЛЕННЯ:

якщо ви не знаєте довжину рядка, спробуйте:

$ x="lkj"
$ echo "${x%?}"
lk

62

Використовувати sed слід так само швидко

sed 's/.$//'

Твій єдиний відгомін - це тоді echo ljk | sed 's/.$//'.
Використовуючи це, рядок 1 рядка може бути будь-якого розміру.


10
Зауважте, що в загальному випадку він видаляє не останній символ рядка , а останній символ кожного рядка рядка .
Стефан Шазелас

44

Кілька варіантів залежно від оболонки:

  • POSIX: t=${t%?}
  • Борн: t=`expr " $t" : ' \(.*\).'`
  • zsh / yash: t=${t[1,-2]}
  • bash / zsh: t=${t:0:-1}
  • ksh93 / bash / zsh / mksh: t=${t:0:${#t}-1}
  • ksh93 / bash / zsh / mksh: t=${t/%?}
  • ksh93: t=${t/~(E).$/}
  • es: @ {t=$1} ~~ $t *?

Зауважте, що хоча всі повинні стягувати останній символ , ви виявите, що деякі реалізації (ті, що не підтримують багатобайтові символи) замість цього знімають останній байт (так що, швидше за все, він може пошкодити останній символ, якщо він був багатобайтовим ).

exprВаріант передбачає $tне закінчується в більш ніж один символ нового рядка. Він також поверне ненульовий статус виходу, якщо результуюча рядок закінчується 0( 000або навіть -0з деякими реалізаціями). Він також може дати несподівані результати, якщо рядок містить недійсні символи.


Приємно і ретельно! Але ... Я припускаю, що всі ці оболонки підтримують POSIX, тому кожен повинен просто використовувати цю, щоб бути найбільш портативною. Найменший кількість персонажів теж!
Русь

@Russ, t=${t%?}це не Bourne, але ти, швидше за все, не натрапиш на шкаралупу Bourne. ${t%?}Хоча працює у всіх інших.
Стефан Хазелас

Не надано жодної опції шкаралупи риби! Напевно, більш популярний у ці дні, ніж ksh93 ...
rien333

@ rien333. Я б зачекав, поки інтерфейс трохи стабілізується. fishце незавершена робота. 2.3.0, який представив stringвбудований, не був випущений під час Q&A. З версією, на якій я її тестую, вам знадобиться string replace -r '(?s).\z' '' -- $t(і я б очікував, що вони захочуть це змінити, вони повинні змінити прапори, які вони передають PCRE) або більш перекручені. Він також погано має справу з символами нового рядка, і я знаю, що вони також планують змінити це.
Стефан Хазелас

Запрошено відповідь POSIX. підтверджено, що працює над Bash 3.2.57 (1)
Avindra Goolcharan

26

Найбільш портативна і найкоротша відповідь майже напевно:

${t%?}

Це працює в bash, sh, ash, dash, busybox / ash, zsh, ksh тощо.

Він працює за допомогою розширення параметрів оболонки Old-School. Зокрема, %вказує на видалення найменшого суфікса параметра, tякий відповідає шаблону глобуса ?(тобто: будь-який символ).

Дивіться "Видалення найменшого шаблону суфіксів" тут для (набагато) більш детального пояснення та додаткових відомостей. Також дивіться документи для вашої оболонки (наприклад:) у man bashрозділі "Розширення параметра".


Як бічну примітку, якщо ви хочете замість цього видалити перший символ, ви використовуєте ${t#?}, оскільки #збігаються з передньої частини рядка (префікса) замість задньої (суфікса).

Також варто відзначити, що обидва %і #мають, %%і ##версії, які відповідають найдовшій версії заданого шаблону замість найкоротшої. Як ${t%%?}і ${t##?}буде робити те ж саме , як їх одного оператора в цьому випадку, хоча (так що не додати даремний додатковий символ). Це тому, що даний ?візерунок відповідає лише одному символу. Поєднайтеся *з деякими недискартними картками, і речі стають цікавішими з %%і ##.

Розуміння розширень параметрів або, принаймні, знання про їх існування та вміння їх шукати, неймовірно корисно для написання та розшифровки сценаріїв оболонок багатьох смаків. Розширення параметрів часто виглядають як вуду-аркадна оболонка для багатьох людей, тому що ... ну ... вони є прихованою оболонкою вуду (хоча досить добре задокументовано, якщо ви знаєте шукати "розширення параметрів"). Безумовно, добре мати пояс в інструменті, коли ви застрягли в оболонці.


Короткий і милий, і працює як на MacOS, так і на Linux!
dbernard

18
t=lkj
echo ${t:0:${#t}-1}

Ви отримуєте підрядку від 0 до довжини рядка -1. Однак зауважте, що ця субстрація є специфічною для удару та не працюватиме на інших оболонках.

Наприклад, dashнавіть не в змозі розібратися

echo ${t:0:$(expr ${#t} - 1)}

Наприклад, на Ubuntu, /bin/shєdash


15

Ви також можете використовувати headдля друку всіх, крім останнього символу.

$ s='i am a string'
$ news=$(echo -n $s | head -c -1)
$ echo $news
i am a strin

Але, на жаль, деякі версії headне містять провідних -варіантів. Це стосується того, headщо поставляється з ОС X.


5

Це досить просто зробити, використовуючи регулярний вираз:

n=2
echo "lkj" | sed "s/\(.*\).\{$n\}/\1/"

5

Деякі уточнення. Щоб видалити більше одного символу, ви можете додати кілька знаків запитання. Наприклад, для видалення останніх двох символів зі змінної:, $SRC_IP_MSGви можете використовувати:

SRC_IP_MSG=${SRC_IP_MSG%??}

4

Просто для завершення деяких можливих застосувань чистого башу:

#!/bin/bash

# Testing substring removal
STR="Exemple string with trailing whitespace "
echo "'$STR'"
echo "Removed trailing whitespace: '${STR:0:${#STR}-1}'"
echo "Removed trailing whitespace: '${STR/%\ /}'"

Перший синтаксис бере підрядку з рядка, синтаксис - Для другого, помічайте знак, що означає "з кінця рядка", і синтаксис є
${STRING:OFFSET:LENGTH}
%
${STRING/PATTERN/SUBSTITUTION}

І ось дві коротші форми вищезгаданого

echo "Removed trailing whitespace: '${STR::-1}'"
echo "Removed trailing whitespace: '${STR%\ }'"

Тут знову зауважте %знак, що означає "Видалити (тобто замінити на" ") найкоротший узор відповідного шаблону (тут представлений пробігом " \ " з кінця ПАРАМЕТРА - тут названо STR


1

Оскільки ми також можемо використовувати php у командному рядку або скриптах оболонки. Іноді корисно для хірургічного розбору.

php -r "echo substr('Hello', 0, -1);" 
// Output hell

З трубопроводами:

echo "hello" | php -r "echo substr(trim(fgets(STDIN)), 0, -1);"
// Output hell

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