Як розділити рядок по оболонці і отримати останнє поле


293

Припустимо, у мене є рядок, 1:2:3:4:5і я хочу отримати його останнє поле ( 5у цьому випадку). Як це зробити за допомогою Bash? Я спробував cut, але не знаю, як вказати останнє поле -f.

Відповіді:


430

Ви можете використовувати рядкові оператори :

$ foo=1:2:3:4:5
$ echo ${foo##*:}
5

Це обрізає все з фронту, до жадібного ":".

${foo  <-- from variable foo
  ##   <-- greedy front trim
  *    <-- matches anything
  :    <-- until the last ':'
 }

7
Хоча це працює для даної проблеми, відповідь Вільяма нижче ( stackoverflow.com/a/3163857/520162 ) також повертається, 5якщо рядок є 1:2:3:4:5:(при використанні операторів рядків дає порожній результат). Це особливо зручно при аналізі контурів, які можуть містити (або не мати) фінішний /символ.
eckes

9
Як би ти зробив протилежне цьому? відлунювати '1: 2: 3: 4:'?
Добз

12
І як зберігати деталь перед останнім роздільником? Мабуть, використовуючи ${foo%:*}. #- від початку; %- з кінця. #, %- найкоротший збіг; ##, %%- найдовший збіг.
Mihai Danila

1
Якщо я хочу отримати останній елемент із шляху, як мені його використовувати? echo ${pwd##*/}не працює.
Путник

2
@Putnik ця команда розглядає pwdяк змінну. Спробуйте dir=$(pwd); echo ${dir##*/}. Працює для мене!
Стен Струм

344

Інший спосіб полягає в тому, щоб повернути до і після cut:

$ echo ab:cd:ef | rev | cut -d: -f1 | rev
ef

Це дозволяє дуже легко отримати останнє, але одне поле, або будь-який діапазон полів, пронумерованих з кінця.


17
Ця відповідь є приємною, оскільки вона використовує "вирізати", який автор (імовірно) вже знайомий. Крім того, мені подобається ця відповідь, тому що я використовую "вирізати" і у мене було саме це запитання, отже, знаходжу цю тему за допомогою пошуку.
Даннід

6
Деякі корми для обрізки та пасти для людей, які використовують пробіли як роздільники:echo "1 2 3 4" | rev | cut -d " " -f1 | rev
funroll

2
рев | вирізати -d -f1 | rev такий розумний! Дякую! Допомогла мені купа (мій приклад використання був rev | -d '' -f 2- | rev
EdgeCaseBerg

1
Я завжди про це забуваю rev, було саме те, що мені потрібно! cut -b20- | rev | cut -b10- | rev
shearn89

Я закінчився цим рішенням, моя спроба вирізати шляхи до файлів з "awk -F" / "'{print $ NF}'" трохи зламалася для мене, оскільки імена файлів, включаючи пробіли, також розрізалися
THX

76

Останнє поле отримати важко за допомогою вирізання, але ось декілька рішень у awk та perl

echo 1:2:3:4:5 | awk -F: '{print $NF}'
echo 1:2:3:4:5 | perl -F: -wane 'print $F[-1]'

5
велика перевага цього рішення перед прийнятою відповіддю: воно також відповідає шляхам, які містять або не містять фінішний /символ: /a/b/c/dі /a/b/c/d/дають той самий результат ( d) при обробці pwd | awk -F/ '{print $NF}'. Прийнята відповідь призводить до порожнього результату у випадку/a/b/c/d/
eckes

@eckes У випадку рішення AWK, на GNU bash, версія 4.3.48 (1) - звільніть, що не відповідає дійсності, оскільки це важливо, коли у вас є проміжна коса риса чи ні. Простіше кажучи, AWK використовуватиме /як роздільник, і якщо ваш шлях, /my/path/dir/він буде використовувати значення після останнього роздільника, що є просто порожнім рядком. Тож найкраще уникати перекинутого косого кута, якщо вам потрібно зробити таке, як я.
стамстер

Як я можу отримати підрядку ДО останнього поля?
blackjacx

1
@blackjacx Є деякі химерності, але щось подібне awk '{$NF=""; print $0}' FS=: OFS=:часто працює досить добре.
Вільям Перселл

29

Якщо припустити досить просте використання (наприклад, не виходить роздільник, наприклад, ви можете використовувати греп:)

$ echo "1:2:3:4:5" | grep -oE "[^:]+$"
5

Розбивка - знайдіть усіх символів не роздільник ([^:]) в кінці рядка ($). -друкує лише відповідну частину.


-E означає використання розширеного синтаксису; [^ ...] означає що-небудь, крім перерахованих символів; + один або декілька таких звернень (візьме максимально можливу довжину для шаблону; цей елемент є розширенням gnu) - наприклад, розділові знаки - двокрапка.
Олександр Стор

18

Односторонній:

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]}

11
$ echo "a b c d e" | tr ' ' '\n' | tail -1
e

Просто переведіть роздільник у новий рядок і виберіть останній запис за допомогою tail -1.


Він не вийде, якщо останній елемент містить а \n, але в більшості випадків є найбільш читаним рішенням.
Yajo

7

Використання 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

4

Якщо ваше останнє поле є одним символом, ви можете зробити це:

a="1:2:3:4:5"

echo ${a: -1}
echo ${a:(-1)}

Перевірте маніпуляцію з рядком в баші .


Це не працює: він дає останній символ з a, а не останньої поля .
gniourf_gniourf

1
Правда, це ідея, якщо ви знаєте довжину останнього поля, це добре. Якщо ні, вам не доведеться використовувати щось інше ...
Ab Irato

4

Тут є багато хороших відповідей, але все ж я хочу поділитися цим за допомогою базового імені :

 basename $(echo "a:b:c:d:e" | tr ':' '/')

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

Це не найкраща відповідь, але вона просто показує, як можна бути творчим за допомогою команд bash.


2

Використання Bash.

$ var1="1:2:3:4:0"
$ IFS=":"
$ set -- $var1
$ eval echo  \$${#}
0

Могли використати echo ${!#}замість eval echo \$${#}.
Рафа

2
echo "a:b:c:d:e"|xargs -d : -n1|tail -1

Спочатку використовуйте xargs розділити його, використовуючи ":", - n1 означає, що кожен рядок має лише одну частину. Потім, вивівши останню частину.


1
for x in `echo $str | tr ";" "\n"`; do echo $x; done

2
Це може виникнути до проблем, якщо в будь-якому з полів є пробіл. Крім того, він безпосередньо не стосується питання отримання останнього поля.
чепнер

1

Для тих, хто комфортний з 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, який застосовується до вводу.


1

Можливо, трохи запізнюється з відповіддю, хоча простим рішенням є змінити порядок введення рядка. Це дозволить вам завжди отримувати останній елемент незалежно від довжини.

[chris@desktop bin]$ echo 1:2:3:4:5 | rev | cut -d: -f1
5

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

[chris@desktop bin]$ echo 1:2:3:4:5:8:24 | rev | cut -d: -f1
42
[chris@desktop bin]$ echo 1:2:3:4:5:8:24 | rev | cut -d: -f1 | rev
24

Сподіваюся, я можу допомогти, ура


1

Рішення з використанням прочитаного вбудованого:

IFS=':' read -a fields <<< "1:2:3:4:5"
echo "${fields[4]}"

Або, щоб зробити це більш загальним:

echo "${fields[-1]}" # prints the last item

0

Якщо вам подобається python і у вас є можливість встановити пакет, ви можете використовувати цю утиліту python .

# install pythonp
pythonp -m pip install pythonp

echo "1:2:3:4:5" | pythonp "l.split(':')[-1]"
5

python може це зробити безпосередньо: echo "1:2:3:4:5" | python -c "import sys; print(list(sys.stdin)[0].split(':')[-1])"
MortenB

@MortenB Ви помиляєтесь. Вся ціль pythonpпакету - змусити вас робити те саме, що і python -cз меншою кількістю символів. Будь ласка, подивіться на README у сховищі.
бомби

0

Збіг Regex sed- жадібний (завжди йде до останнього явища), який ви можете використати для себе на користь

$ foo=1:2:3:4:5
$ echo ${foo} | sed "s/.*://"
5
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.