Как получить последний символ строки в оболочке?

127

Я написал следующие строки, чтобы получить последний символ строки:

str=$1
i=$((${#str}-1))
echo ${str:$i:1}

Работает для abcd/:

$ bash last_ch.sh abcd/
/

Она не работаетabcd* :

$ bash last_ch.sh abcd*
array.sh assign.sh date.sh dict.sh full_path.sh last_ch.sh

В нем перечислены файлы в текущей папке .

thinker3
источник

Ответы:

99

Это одна из причин, по которой вам нужно указывать переменные в кавычках:

echo "${str:$i:1}"

В противном случае bash расширяет переменную и в этом случае выполняет подстановку перед выводом на печать. Также лучше указать параметр скрипта в кавычках (если у вас есть совпадающее имя файла):

sh lash_ch.sh 'abcde*'

Также см. Порядок расширений в справочном руководстве по bash . Переменные раскрываются перед расширением имени файла.

Чтобы получить последний символ, вы должны просто использовать его -1как индекс, так как отрицательные индексы отсчитываются от конца строки:

echo "${str: -1}"

Пробел после двоеточия ( :) НЕОБХОДИМ.

Такой подход не будет работать без места.

perreal
источник
62
Кстати, отрицательные индексы считаются справа, так "${1: -1}"что достаточно.
choroba
7
Вы должны ответить как формальное решение, а не здесь, в комментариях.
BMW
К вашему сведению: вы также можете сделать это в виде однострочного файла echo "${str:$((${#str}-1)):1}". Это также позволяет вам изменять возвращаемые завершающие символы. Эта работа для меня в bash v4.1.0 (1)
SaxDaddy
16
@ choroba ответ: Обратите внимание на проклятое пространство! Это всегда меня "${1:-1}
сбивает
192

Per @perreal, цитирование переменных важно, но поскольку я прочитал этот пост примерно 5 раз, прежде чем найти более простой подход к рассматриваемому вопросу в комментариях ...

str='abcd/'
echo "${str: -1}"

Вывод: /

str='abcd*'
echo "${str: -1}"

Вывод: *

Спасибо всем, кто участвовал в этом выше; Я правильно добавил +1 по всей цепочке!

quickshiftin
источник
25
NB: обратите внимание на пространство внутри ${std: -1}, без него не получится. Я столкнулся с этим и обнаружил, что это ${std:~0}тоже работает (с пробелом или без него). Не уверен, что это задокументированное поведение, но я не стал его искать.
Барт
4
это задокументировано. man bash говорит: арифметические выражения, начинающиеся с - должны быть отделены пробелом от предыдущего: чтобы их можно было отличить от расширения Use Default Values
mighq
3
Это здорово, спасибо, что поделились. Однако справочную страницу BASH сложно читать, я был там несколько раз. Я думаю, они могли бы сделать в колледже курс по сценариям оболочки, лол.
quickshiftin
3
Это решение не работает в .shсценарии при попытке присвоить извлеченный символ переменной. Например, declare LAST=$(echo "${str: -1}") это приведет к появлению неприятной красной полосы с синтаксической ошибкой, начиная с:
krb686
3
если проверка синтаксиса редактора не нравится, попробуйте${str:0-1}
ttt
37

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

echo -n $str | tail -c 1

Обратите внимание, что -nэто просто так, что эхо не включает новую строку в конце.

user5394399
источник
15
Даже близко к тому, чтобы быть чище, чем $ {str: -1}
shrewmouse
8
Он чище, поскольку не полагается на bash-ism, поэтому будет работать с любой оболочкой Posix.
Джон Эйкенберри
Какая самая «башистская»? "$ {str: -1}" И ... Что лучше всего? "$ {str: -1}" Баш - лучший! :-)
Роджер
Для всех, кто пытался напечатать последние символы в большом файле, это помогло мне: cat user / folder / file.json | tail -c 1 Вы можете заменить 1 на количество символов, которое вы хотите напечатать.
Диего Серрано,
6

другое решение с использованием сценария awk:

последний 1 символ:

echo $str | awk '{print substr($0,length,1)}'

последние 5 символов:

echo $str | awk '{print substr($0,length-5,5)}'
mr.baby123
источник
1
Не то, что просил OP, но это лучшее решение, которое я нашел для использования с файлом. Большинство других подходов не сработают с добавлением файла в него, например: cat file | awk '{print substr($0,length,1)}'Спасибо!
xmar
5

До сих пор каждый ответ подразумевает, что слово «оболочка» в вопросе приравнивается к Bash.

Вот как это можно сделать в стандартной оболочке Bourne:

printf $str | tail -c 1
phep
источник
2
echo -nпо предложению user5394399 позволяет избежать проблем, если он $strсодержит символы специального формата.
Конрад Мейер,
-nПереключатель является bashism. Это не будет работать в стандартной оболочке Bourne в виде тире и т. Д. Это был смысл моего ответа.
phep
1
Это не совсем уж глупость, но POSIX признает, что это определено реализацией («Если первый операнд - -n, или если какой-либо из операндов содержит символ <обратная косая черта>, результаты определяются реализацией»). Вместо этого ваш ответ можно исправить с помощью printf "%s" "$str" | ...:-).
Конрад Мейер
4

Одна линия:

${str:${#str}-1:1}

Сейчас:

echo "${str:${#str}-1:1}"
Эдуардо Куомо
источник
1
Почему вы используете две пары круглых скобок? Кроме того, начиная с 4.2, вы можете использовать отрицательные смещения, как показано в ответе quickshiftin: echo "${str: -1}"достаточно (обратите внимание на пробел между :и -).
gniourf_gniourf
Две пары круглых скобок используются для арифметических операций
Эдуардо Куомо,
Нет. См. Соответствующую часть руководства, где вы прочитаете: длина и смещение - это арифметические выражения (см. Арифметика оболочки ); следовательно, вы уже занимаетесь арифметикой оболочки и вообще не нуждаетесь в скобках. Кроме того, если то , что вы сказали , были правдой, это было бы более логично было бы иметь арифметическое расширение с $((...)).
gniourf_gniourf
@gniourf_gniourf ты прав! Теперь я понимаю ваш предыдущий вопрос. Ответ обновлен
Эдуардо Куомо
4

Пытаться:

"${str:$((${#str}-1)):1}"

Например:

someone@mypc:~$ str="A random string*"; echo "$str"
A random string*
someone@mypc:~$ echo "${str:$((${#str}-1)):1}"
*
someone@mypc:~$ echo "${str:$((${#str}-2)):1}"
g
mark_infinite
источник
-2
echo $str | cut -c $((${#str}))

это хороший подход

Прадип
источник