Відповіді:
Використовуйте cut
з _
як роздільник полів і отримати необхідні поля:
A="$(cut -d'_' -f2 <<<'one_two_three_four_five')"
B="$(cut -d'_' -f4 <<<'one_two_three_four_five')"
Ви також можете використовувати echo
та pipe замість рядка Here:
A="$(echo 'one_two_three_four_five' | cut -d'_' -f2)"
B="$(echo 'one_two_three_four_five' | cut -d'_' -f4)"
Приклад:
$ s='one_two_three_four_five'
$ A="$(cut -d'_' -f2 <<<"$s")"
$ echo "$A"
two
$ B="$(cut -d'_' -f4 <<<"$s")"
$ echo "$B"
four
$ echo $FILE
my_user/my_folder/file.csv
$ A="$(cut -d'/' -f2 <<<"$FILE")"
$ echo $A
[file]*
Чи знаєте ви, що тут відбувається?
echo "${s##*_}"
Використовуючи лише конструкції POSIX sh, ви можете використовувати конструкції підстановки параметрів для розбору одного роздільника за один раз. Зауважте, що цей код передбачає необхідну кількість полів, інакше останнє поле повторюється.
string='one_two_three_four_five'
remainder="$string"
first="${remainder%%_*}"; remainder="${remainder#*_}"
second="${remainder%%_*}"; remainder="${remainder#*_}"
third="${remainder%%_*}"; remainder="${remainder#*_}"
fourth="${remainder%%_*}"; remainder="${remainder#*_}"
Крім того, ви можете використовувати незамінену параметр заміни параметрів з вимкненим розширенням підстановки і IFS
встановити його на знак роздільника (це працює лише в тому випадку, якщо роздільник є одним символом без пробілу або якщо будь-яка послідовність пробілів є роздільником).
string='one_two_three_four_five'
set -f; IFS='_'
set -- $string
second=$2; fourth=$4
set +f; unset IFS
Це клобує позиційні параметри. Якщо ви робите це у функції, впливають лише позиційні параметри функції.
Ще один підхід - використовувати read
вбудований.
IFS=_ read -r first second third fourth trail <<'EOF'
one_two_three_four_five
EOF
unset IFS
не повертається IFS
до типового. Якщо після цього хтось має OldIFS="$IFS"
мати нульове значення всередині OldIFS. Крім того, передбачається, що попереднє значення IFS є типовим, чого дуже можливо (і корисно) не бути. Єдине правильне рішення - зберігання old="$IFS"
та відновлення пізніше з IFS = "$ old". Або ... скористайтеся підкольором (...)
. Або, ще краще, прочитайте мою відповідь.
unset IFS
не відновлює IFS
значення за замовчуванням, але повертає розділення поля до ефекту за замовчуванням. Так, це обмеження, але зазвичай прийнятне на практиці. Проблема з передплатою полягає в тому, що нам потрібно отримати дані з неї. Я дійсно показую рішення, яке не змінює стан в кінці, за допомогою read
. (Він працює в оболонках POSIX, але IIRC не в оболонці Bourne, тому що він би запускався в підрозділ read
завдяки документу here.) Використання <<<
як у вашому відповіді є варіантом, який працює лише в ksh / bash / zsh.
user/my_folder/[this_is_my_file]*
? Що я отримую, виконуючи ці кроки,[this_is_my_file]*
/
.
Хотіла побачити awk
відповідь, ось ось одна:
A=$(awk -F_ '{print $2}' <<< 'one_two_three_four_five')
B=$(awk -F_ '{print $4}' <<< 'one_two_three_four_five')
awk -F_ '{print $NF}' <<< 'one_two_3_4_five'
Найпростіший спосіб (для снарядів з <<<):
IFS='_' read -r a second a fourth a <<<"$string"
Використання тимчасової змінної $a
замість того, $_
що одна оболонка скаржиться.
Повний сценарій:
string='one_two_three_four_five'
IFS='_' read -r a second a fourth a <<<"$string"
echo "$second $fourth"
Не змінюється IFS, не set -f
виникає проблем з (розширення Pathname) Не змінюються позиційні параметри ("$ @").
Для рішення, що переноситься на всі оболонки (так, всі POSIX включені) без зміни IFS або set -f
, використовуйте (трохи складніший) еквівалент heredoc:
string='one_two_three_four_five'
IFS='_' read -r a second a fourth a <<-_EOF_
$string
_EOF_
echo "$second $fourth"
Зрозумійте, що ці рішення (і тут, і doc, і використання <<<
видалять усі останні лінії).
І що це розроблено для змінного вмісту "одного вкладиша".
Рішення для мультилайнерів можливі, але потребують складніших конструкцій.
Дуже просте рішення можливе в баш-версії 4.4
readarray -d _ -t arr <<<"$string"
echo "array ${arr[1]} ${arr[3]}" # array numbers are zero based.
Для оболонок POSIX немає еквівалента, оскільки багато оболонок POSIX не мають масивів.
Для оболонок, що мають масиви, може бути так само просто:
(перевірено, що працює в attsh, lksh, mksh, ksh та bash)
set -f; IFS=_; arr=($string)
Але з великою кількістю додаткової сантехніки для збереження та скидання змінних та параметрів:
string='one_* *_three_four_five'
case $- in
*f*) noglobset=true; ;;
*) noglobset=false;;
esac
oldIFS="$IFS"
set -f; IFS=_; arr=($string)
if $noglobset; then set -f; else set +f; fi
echo "two=${arr[1]} four=${arr[3]}"
У zsh масиви починаються з 1 і не розділяють рядок за замовчуванням.
Тому потрібно внести деякі зміни, щоб це працювало в zsh.
read
, прості до тих пір, поки ОП не захоче витягти 76-й та 127-й елементи з довгої струни ...
readarray
можна було б простіше використовувати для цієї ситуації.
За допомогою zsh
вас можна розділити рядок (on _
) на масив:
elements=(${(s:_:)string})
а потім отримати доступ до кожного / будь-якого елемента за допомогою індексу масиву:
print -r ${elements[4]}
Майте на увазі, що в zsh
(на відміну від ksh
/ bash
) індекси масиву починаються з 1 .
set -f
попередження до першого рішення. ... зірочки, *
можливо?
set -f
? Я не використовую read
/ IFS
. Спробуйте мої рішення, як- *_*_*
небудь або як завгодно ...
Чи дозволений розчин пітона?
# python -c "import sys; print sys.argv[1].split('_')[1]" one_two_three_four_five
two
# python -c "import sys; print sys.argv[1].split('_')[3]" one_two_three_four_five
four
Ще один приклад awk; простіший для розуміння.
A=\`echo one_two_three_four_five | awk -F_ '{print $1}'\`
B=\`echo one_two_three_four_five | awk -F_ '{print $2}'\`
C=\`echo one_two_three_four_five | awk -F_ '{print $3}'\`
... and so on...
Можна використовувати і зі змінними.
Припустимо:
this_str = "one_two_three_four_five"
Потім працює наступне:
A = `echo $ {this_str} | awk -F_ '{print $ 1}' `
B =` echo $ {this_str} | awk -F_ '{print $ 2}' `
C =` echo $ {this_str} | awk -F_ '{print $ 3}' `
... і так далі ...