Удалить последний символ строки, используя строковые манипуляции в сценарии оболочки

188

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

#! /bin/sh 

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

но он печатает "lkj", что я делаю не так?

user3581976
источник

Ответы:

115

В оболочке POSIX синтаксис ${t:-2}означает что-то другое - он расширяется до значения tif t, установленного и отличного от NULL, а в противном случае до значения 2. Чтобы обрезать один символ с помощью раскрытия параметров, вы, вероятно, захотите следующий синтаксис:${t%?}

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

См. Раздел «Расширение параметров оболочки» Справочного руководства Bash для получения дополнительной информации:

steeldriver
источник
4
Не могли бы вы объяснить, что за магия стоит за%? ?
afraisse
8
@afraisse ${parameter%word}удаляет сопоставление с самым коротким суффиксным шаблоном word- см. раздел «Расширение параметров»man bash
steeldriver
3
Это хорошо работает для Bash 4.1.2: $ {t%?} Для тех, кто застрял в CentOS / RHEL 6.x
Joey T
186

С bash4.2 и выше, вы можете сделать:

${var::-1}

Пример:

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

Обратите внимание, что для более старых версий bash(например, bash 3.2.5в OS X) вы должны оставлять пробелы между двоеточиями и после них:

${var: : -1}
cuonglm
источник
13
Это работает для bashверсии 4.2-alpha и выше, слишком плохая версия, к которой у меня есть доступ, более ранняя. : - /
hjk
2
@iamaziz: из журнала изменений bash отрицательная длина в ${var:offset:lenght}была добавлена ​​только в bash 4.2. Может быть, OSX добавить свой собственный патч для bash.
Cuonglm
1
@cuonglm также не работает: /
iamaziz
1
Не работает на Mac.
Синдзоу
1
MACsters, посмотрите вниз на ответ Русса
P i
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 строки может быть любого размера.

1111161171159459134
источник
10
Обратите внимание, что в общем случае он удаляет не последний символ строки , а последний символ каждой строки строки .
Стефан Шазелас
44

Несколько вариантов в зависимости от оболочки:

  • POSIX: t=${t%?}
  • Bourne: t=`expr " $t" : ' \(.*\).'`
  • ЗШ / йаш: t=${t[1,-2]}
  • Баш / ЗШ: t=${t:0:-1}
  • ksh93 / Баш / ЗШ / МКШ: t=${t:0:${#t}-1}
  • ksh93 / Баш / ЗШ / МКШ: t=${t/%?}
  • ksh93: t=${t/~(E).$/}
  • эс: @ {t=$1} ~~ $t *?

Обратите внимание, что хотя все должны удалять последний символ , вы обнаружите, что некоторые реализации (те, которые не поддерживают многобайтовые символы) вместо этого отбрасывают последний байт (так что, вероятно, испортил бы последний символ, если он был многобайтовым). ).

Предполагается, что exprвариант $tне заканчивается более чем одним символом новой строки. Он также вернет ненулевой статус выхода, если полученная строка окажется 0( 000или даже -0с некоторыми реализациями). Это также может дать неожиданные результаты, если строка содержит недопустимые символы.

Стефан Шазелас
источник
Хорошо и тщательно! Но ... я полагаю, что все эти оболочки поддерживают POSIX, поэтому каждый должен использовать этот, чтобы быть наиболее переносимым. Наименьшее количество символов тоже!
Расс
@Russ, t=${t%?}это не Bourne, но вы вряд ли встретите Bourne shell в наши дни. ${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 и т. Д.

Он работает с использованием расширения параметров старой школы. В частности, %указывается, чтобы удалить наименьший соответствующий суффикс соответствия параметра, tкоторый соответствует шаблону глобуса ?(т. Е. Любому символу).

См. «Удалить самый маленький шаблон суффикса» здесь для более подробного объяснения и дополнительной информации. Также см. Документы для вашей оболочки (например:) в man bashразделе «Расширение параметров».


В качестве примечания: если вы хотите вместо этого удалить первый символ, вы должны использовать ${t#?}, так как #совпадения начинаются с начала строки (префикс), а не с конца (суффикс).

Также стоит отметить, что как %и , так и #иметь %%и ##версии, которые соответствуют самой длинной версии данного шаблона, а не самой короткой. Оба ${t%%?}и ${t##?}будут делать то же самое, что и их единственный оператор в этом случае (так что не добавляйте лишний лишний символ). Это потому, что данный ?шаблон соответствует только одному символу. Смешайте *с некоторыми не подстановочными знаками, и все становится более интересным с %%и ##.

Понимание расширений параметров или, по крайней мере, знание об их существовании и умение их искать, невероятно полезно для написания и расшифровки сценариев оболочки многих разновидностей. Расширения параметров часто выглядят как тайная оболочка вуду для многих людей, потому что ... ну ... они являются тайной оболочкой вуду (хотя довольно хорошо документированы, если вы знаете, что нужно искать "расширение параметров"). Определенно хорошо иметь в поясе инструментов, когда вы застряли в оболочке.

Russ
источник
Коротко и мило, и работает как на MacOS, так и на Linux!
Дбернард
18
t=lkj
echo ${t:0:${#t}-1}

Вы получаете подстроку от 0 до длины строки -1. Однако обратите внимание, что это вычитание зависит от bash и не будет работать на других оболочках.

Например, 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который идет с OS X.

greenbeansugar
источник
5

Это достаточно просто сделать с помощью регулярного выражения:

n=2
echo "lkj" | sed "s/\(.*\).\{$n\}/\1/"
unxnut
источник
5

Некоторые уточнения. Чтобы удалить более одного символа, вы можете добавить несколько знаков вопроса. Например, чтобы удалить два последних символа из переменной:, $SRC_IP_MSGвы можете использовать:

SRC_IP_MSG=${SRC_IP_MSG%??}
yuliskov
источник
4

Просто для завершения некоторых возможных применений чистого Bash:

#!/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

CermakM
источник
1

Также мы можем использовать php в командной строке или shell-скриптах. Иногда это полезно для хирургического анализа.

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

С обвязкой:

echo "hello" | php -r "echo substr(trim(fgets(STDIN)), 0, -1);"
// Output hell
NVRM
источник