Ответы:
Используйте cut
с _
в качестве разделителя полей и получите нужные поля:
A="$(cut -d'_' -f2 <<<'one_two_three_four_five')"
B="$(cut -d'_' -f4 <<<'one_two_three_four_five')"
Вы также можете использовать echo
и конвейер вместо строки 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
в подоболочке из-за нижеследующего документа.) Использование <<<
ответа as in you - это вариант, который работает только в 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
(расширение имени пути). Без изменений позиционных параметров ("$ @").
Для решения, переносимого на все оболочки (да, включая все 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"
Поймите, что это решение (как здесь, так и при использовании <<<
, удалит все завершающие символы новой строки.
И что оно предназначено для переменного содержимого "в один слой".
Решения для мультилиний могут быть, но требуют более сложных конструкций.
В bash версии 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
, просты, если OP не хочет извлекать 76-й и 127-й элементы из длинной строки ...
readarray
может быть проще использовать в такой ситуации.
С помощью zsh
вы можете разбить строку (в _
) на массив:
elements=(${(s:_:)string})
а затем получить доступ к каждому / любому элементу через индекс массива:
print -r ${elements[4]}
Имейте в виду, что в zsh
(в отличие от ksh
/ bash
) индексы массива начинаются с 1 .
set -f
предупреждение к первому решению. ... звездочки *
может быть?
set -f
? Я не использую read
/ IFS
. Попробуйте мои решения со строкой, как *_*_*
или что-то еще ...
Разрешено ли решение на Python?
# 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}' `
... и так далее ...