Réponses:
Utilisez cut
avec _
comme délimiteur de champ et obtenez les champs désirés:
A="$(cut -d'_' -f2 <<<'one_two_three_four_five')"
B="$(cut -d'_' -f4 <<<'one_two_three_four_five')"
Vous pouvez également utiliser echo
et pipe au lieu de la chaîne Here:
A="$(echo 'one_two_three_four_five' | cut -d'_' -f2)"
B="$(echo 'one_two_three_four_five' | cut -d'_' -f4)"
Exemple:
$ 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]*
Savez-vous ce qui se passe ici?
echo "${s##*_}"
En utilisant uniquement les constructions POSIX sh, vous pouvez utiliser des constructions de substitution de paramètres pour analyser un délimiteur à la fois. Notez que ce code suppose qu'il existe le nombre requis de champs, sinon le dernier champ est répété.
string='one_two_three_four_five'
remainder="$string"
first="${remainder%%_*}"; remainder="${remainder#*_}"
second="${remainder%%_*}"; remainder="${remainder#*_}"
third="${remainder%%_*}"; remainder="${remainder#*_}"
fourth="${remainder%%_*}"; remainder="${remainder#*_}"
Vous pouvez également utiliser une substitution de paramètre non entre guillemets avec le développement de caractères génériques désactivé et IFS
défini sur le caractère de délimitation (cela ne fonctionne que si le délimiteur est un seul caractère non blanc ou si une séquence d'espaces blancs est un délimiteur).
string='one_two_three_four_five'
set -f; IFS='_'
set -- $string
second=$2; fourth=$4
set +f; unset IFS
Cela écrase les paramètres de position. Si vous faites cela dans une fonction, seuls les paramètres de position de la fonction sont affectés.
Une autre approche consiste à utiliser le mode read
intégré.
IFS=_ read -r first second third fourth trail <<'EOF'
one_two_three_four_five
EOF
unset IFS
ne retourne pas IFS
aux valeurs par défaut. Si après cela, quelqu'un OldIFS="$IFS"
aura une valeur nulle dans OldIFS. En outre, il est supposé que la valeur précédente de IFS est la valeur par défaut, ce qui est très possible (et utile) de ne pas l'être. La seule solution correcte est de stocker dans old="$IFS"
et de restaurer ultérieurement avec IFS = "$ old". Ou ... utilisez un sous-shell (...)
. Ou, mieux encore, lisez ma réponse.
unset IFS
ne rétablit IFS
pas la valeur par défaut, mais renvoie la division du champ à l'effet par défaut. Oui, c'est une limitation, mais généralement acceptable dans la pratique. Le problème avec un sous-shell est que nous devons en extraire des données. Je montre une solution qui ne change pas l'état à la fin, avec read
. (Cela fonctionne dans les shells POSIX, mais pas IIRC dans le shell Bourne, car il s'exécuterait read
dans un sous-shell à cause du document here.) Utiliser <<<
as dans vous répondez est une variante qui fonctionne uniquement dans ksh / bash / zsh.
user/my_folder/[this_is_my_file]*
? Ce que j'obtiens quand je suis ces étapes est[this_is_my_file]*
/
.
Je voulais voir une awk
réponse, alors en voici une:
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'
Le moyen le plus simple (pour les shells avec <<<) est:
IFS='_' read -r a second a fourth a <<<"$string"
Utiliser une variable temporelle $a
au lieu de $_
parce qu'un shell se plaint.
Dans un script complet:
string='one_two_three_four_five'
IFS='_' read -r a second a fourth a <<<"$string"
echo "$second $fourth"
Aucun IFS ne change, pas de problème avec set -f
(extension de nom de chemin) Aucune modification des paramètres de position ("$ @").
Pour une solution portable à tous les shells (oui, tous les POSIX inclus) sans changer d’IFS ou set -f
, utilisez l’équivalent heredoc (un peu plus complexe):
string='one_two_three_four_five'
IFS='_' read -r a second a fourth a <<-_EOF_
$string
_EOF_
echo "$second $fourth"
Comprenez que cette solution (à la fois ici-doc et l'utilisation de <<<
, supprimera toutes les nouvelles lignes qui suivent.
Et cela est conçu pour un contenu variable "one liner". Les
solutions pour les multilignes sont possibles mais nécessitent des constructions plus complexes.
Une solution très simple est possible dans la version 4.4 de bash
readarray -d _ -t arr <<<"$string"
echo "array ${arr[1]} ${arr[3]}" # array numbers are zero based.
Il n'y a pas d'équivalent pour les shells POSIX, car de nombreux shells POSIX n'ont pas de tableaux.
Pour les shells qui ont des tableaux peuvent être aussi simples que:
(testé avec attsh, lksh, mksh, ksh et bash)
set -f; IFS=_; arr=($string)
Mais avec beaucoup de tuyauterie supplémentaire pour conserver et réinitialiser les variables et les options:
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]}"
Dans zsh, les tableaux commencent par 1 et ne divisent pas la chaîne par défaut.
Il faut donc faire quelques changements pour que cela fonctionne dans zsh.
read
sont simples tant que OP ne veut pas extraire les 76ème et 127ème éléments d'une longue chaîne ...
readarray
pourrait être plus facile à utiliser dans cette situation.
Avec zsh
vous pouvez diviser la chaîne (sur _
) en un tableau:
elements=(${(s:_:)string})
puis accédez à chacun des éléments via un index de tableau:
print -r ${elements[4]}
Gardez à l'esprit que dans zsh
(contrairement à ksh
/ bash
) les index de tableau commencent à 1 .
set -f
avertissement à la première solution. ... des astérisques *
peut-être?
set -f
? Je n'utilise pas read
/ IFS
. Essayez mes solutions avec une ficelle *_*_*
ou quelque chose comme ça ...
Un autre exemple awk; plus simple à comprendre.
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...
Peut être utilisé avec des variables aussi.
Supposons:
this_str = "one_two_three_four_five"
Alors les travaux suivants fonctionnent:
A = `echo $ {this_str} | awk -F_ '{print $ 1}' '
B = `echo $ {this_str} | awk -F_ '{print $ 2}' '
C = `echo $ {this_str} | awk -F_ '{print $ 3}' `
... et ainsi de suite ...