Réponses:
Utilisez cutavec _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 echoet 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 IFSdé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 readintégré.
IFS=_ read -r first second third fourth trail <<'EOF'
one_two_three_four_five
EOF
unset IFSne retourne pas IFSaux 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 IFSne rétablit IFSpas 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 readdans 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 awkré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 $aau 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 ...
readarraypourrait être plus facile à utiliser dans cette situation.
Avec zshvous 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 -favertissement à 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 ...