Предположим, у меня есть строка 1:2:3:4:5
, и я хочу получить ее последнее поле (в данном случае 5
). Как мне это сделать с помощью Bash? Я пробовал cut
, но не знаю, как указать последнее поле с помощью -f
.
Как разбить строку в оболочке и получить последнее поле
Ответы (17)
Вы можете использовать строковые операторы:
$ foo=1:2:3:4:5
$ echo ${foo##*:}
5
Это с жадностью обрезает все от лицевой стороны до символа ":".
${foo <-- from variable foo
## <-- greedy front trim
* <-- matches anything
: <-- until the last ':'
}
5
, если строка равна 1:2:3:4:5:
(при использовании строковых операторов возвращается пустой результат). Это особенно удобно при анализе путей, которые могут содержать (или не содержать) завершающий символ /
.
- person eckes; 23.01.2013
${foo%:*}
. #
- с начала; %
- с конца. #
, %
- кратчайшее совпадение; ##
, %%
- самое длинное совпадение.
- person Mihai Danila; 09.07.2014
echo ${pwd##*/}
не работает.
- person Putnik; 12.02.2016
pwd
как переменную. Попробуйте dir=$(pwd); echo ${dir##*/}
. Работает для меня!
- person Stan Strum; 17.12.2017
echo ${$(pwd)##*/}
- person Cadoiz; 16.06.2021
Другой способ - поменять местами до и после cut
:
$ echo ab:cd:ef | rev | cut -d: -f1 | rev
ef
Это позволяет очень легко получить предпоследнее поле или любой диапазон полей, пронумерованных с конца.
echo "1 2 3 4" | rev | cut -d " " -f1 | rev
- person funroll; 12.08.2013
rev
, это было именно то, что мне было нужно! cut -b20- | rev | cut -b10- | rev
- person shearn89; 17.08.2017
rev
небезопасен с многобайтовыми символами Юникода! Поэтому некоторые угловые случаи могут не работать с rev
.
- person t0r0X; 02.03.2018
LC_ALL=en_US.utf8
запуск echo 'hé' | rev
правильно возвращает éh
. Мне нужно запустить echo 'hé' | LC_ALL=C rev
, чтобы получить сообщение об ошибке: rev: stdin: Invalid or incomplete multibyte or wide character
.
- person a3nm; 02.03.2018
-sh: rev: command not found
на моем NAS кажется, что rev не так распространен, в противном случае я согласен, что он лучше отвечает на вопрос о сокращении
- person papo; 19.11.2018
cut
я могу превратить www.google.com в google.com!
- person b_laoshi; 29.11.2018
cat domains.txt | rev | cut -d. -f2 | rev | sort | uniq -c | sort -rn
- person gies0r; 30.04.2020
Трудно получить последнее поле с помощью cut, но вот несколько решений на awk и perl
echo 1:2:3:4:5 | awk -F: '{print $NF}'
echo 1:2:3:4:5 | perl -F: -wane 'print $F[-1]'
/
: /a/b/c/d
и /a/b/c/d/
дают одинаковый результат (d
) при обработке pwd | awk -F/ '{print $NF}'
. Принятый ответ дает пустой результат в случае /a/b/c/d/
- person eckes; 23.01.2013
/
в качестве разделителя, а если ваш путь - /my/path/dir/
, он будет использовать значение после последнего разделителя, которое является просто пустой строкой. Так что лучше избегать косой черты в конце, если вам нужно сделать что-то подобное, как это делаю я.
- person stamster; 21.05.2018
awk '{$NF=""; print $0}' FS=: OFS=:
часто работает достаточно хорошо.
- person William Pursell; 09.06.2020
Предполагая довольно простое использование (например, без экранирования разделителя), вы можете использовать grep:
$ echo "1:2:3:4:5" | grep -oE "[^:]+$"
5
Разбивка - найдите все символы, кроме разделителя ([^:]) в конце строки ($). -o печатает только соответствующую часть.
В одну сторону:
var1="1:2:3:4:5"
var2=${var1##*:}
Другой, используя массив:
var1="1:2:3:4:5"
saveIFS=$IFS
IFS=":"
var2=($var1)
IFS=$saveIFS
var2=${var2[@]: -1}
Еще один с массивом:
var1="1:2:3:4:5"
saveIFS=$IFS
IFS=":"
var2=($var1)
IFS=$saveIFS
count=${#var2[@]}
var2=${var2[$count-1]}
Использование регулярных выражений Bash (версия> = 3.2):
var1="1:2:3:4:5"
[[ $var1 =~ :([^:]*)$ ]]
var2=${BASH_REMATCH[1]}
$ echo "a b c d e" | tr ' ' '\n' | tail -1
e
Просто переведите разделитель в новую строку и выберите последнюю запись с tail -1
.
\n
, но в большинстве случаев это наиболее удобочитаемое решение.
- person Yajo; 30.07.2014
Вы можете попробовать что-то вроде этого, если хотите использовать cut
:
echo "1:2:3:4:5" | cut -d ":" -f5
Вы также можете использовать grep
вот так:
echo " 1:2:3:4:5" | grep -o '[^:]*$'
Использование sed
:
$ echo '1:2:3:4:5' | sed 's/.*://' # => 5
$ echo '' | sed 's/.*://' # => (empty)
$ echo ':' | sed 's/.*://' # => (empty)
$ echo ':b' | sed 's/.*://' # => b
$ echo '::c' | sed 's/.*://' # => c
$ echo 'a' | sed 's/.*://' # => a
$ echo 'a:' | sed 's/.*://' # => (empty)
$ echo 'a:b' | sed 's/.*://' # => b
$ echo 'a::c' | sed 's/.*://' # => c
Если ваше последнее поле состоит из одного символа, вы можете сделать это:
a="1:2:3:4:5"
echo ${a: -1}
echo ${a:(-1)}
Проверьте обработку строк в bash.
a
, а не последнее поле.
- person gniourf_gniourf; 13.11.2013
Здесь есть много хороших ответов, но я все же хочу поделиться этим, используя basename:
basename $(echo "a:b:c:d:e" | tr ':' '/')
Однако он завершится ошибкой, если в вашей строке уже есть "/". Если косая черта / является вашим разделителем, вам просто нужно (и следует) использовать базовое имя.
Это не лучший ответ, но он просто показывает, как вы можете проявить творческий подход, используя команды bash.
Используя Bash.
$ var1="1:2:3:4:0"
$ IFS=":"
$ set -- $var1
$ eval echo \$${#}
0
echo ${!#}
вместо eval echo \$${#}
.
- person Rafa; 28.04.2017
echo "a:b:c:d:e"|xargs -d : -n1|tail -1
Сначала используйте xargs, разделите его с помощью ":", - n1 означает, что каждая строка имеет только одну часть. Затем, pring последнюю часть.
Сопоставление регулярных выражений в sed
является жадным (всегда идет до последнего вхождения), что вы можете использовать здесь в своих интересах:
$ foo=1:2:3:4:5
$ echo ${foo} | sed "s/.*://"
5
Решение с использованием встроенного чтения:
IFS=':' read -a fields <<< "1:2:3:4:5"
echo "${fields[4]}"
Или, чтобы сделать его более общим:
echo "${fields[-1]}" # prints the last item
Для тех, кто знаком с Python, хорошим выбором будет https://github.com/Russell91/pythonpy. чтобы решить эту проблему.
$ echo "a:b:c:d:e" | py -x 'x.split(":")[-1]'
Из справки pythonpy: -x treat each row of stdin as x
.
С помощью этого инструмента легко написать код Python, который применяется к входным данным.
Изменить (декабрь 2020 г.): Pythonpy больше не в сети. Вот альтернатива:
$ echo "a:b:c:d:e" | python -c 'import sys; sys.stdout.write(sys.stdin.read().split(":")[-1])'
он содержит больше шаблонного кода (т.е. sys.stdout.read/write
), но требует только std-библиотек из python.
Если вам нравится python и у вас есть возможность установить пакет, вы можете использовать эту утилиту python.
# install pythonp
pythonp -m pip install pythonp
echo "1:2:3:4:5" | pythonp "l.split(':')[-1]"
5
echo "1:2:3:4:5" | python -c "import sys; print(list(sys.stdin)[0].split(':')[-1])"
- person MortenB; 06.03.2019
pythonp
- заставить вас делать то же самое, что и python -c
, с меньшим количеством символов. Пожалуйста, посмотрите README в репозитории.
- person bombs; 08.03.2019