Comment obtenir le caractère à une position donnée d'une chaîne dans un script shell?

22

Comment obtenir le caractère à une position donnée d'une chaîne dans un script shell?

Tom Brito
la source

Réponses:

36

En bash avec "Parameter Expansion" $ {paramètre: offset: length}

$ var=abcdef
$ echo ${var:0:1}
a
$ echo ${var:3:1}
d

Edit: sans expansion des paramètres (pas très élégant, mais c'est ce qui m'est venu en premier)

$ charpos() { pos=$1;shift; echo "$@"|sed 's/^.\{'$pos'\}\(.\).*$/\1/';}
$ charpos 8 what ever here
r
forcefsck
la source
1
gnu.org/software/bash/manual/… a plus de détails
jsbillings
Pas POSIX apparemment: pubs.opengroup.org/onlinepubs/009695399/utilities/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
Vous pouvez également définir le décalage `` de fin '' comme çaecho ${var: -2:1}
Vassilis
Cette syntaxe provient de ksh93 et ​​est également prise en charge par zshet mksh.
Stéphane Chazelas
6

Une alternative à l'expansion des paramètres est expr substr

substr STRING POS LENGTH
    substring of STRING, POS counted from 1

Par exemple:

$ expr substr hello 2 1
e
dogbane
la source
cool, aurait dû vérifier expr plus en profondeur.
forcefsck
1
Bien que cela semble fonctionner avec l'expr de GNU coreutils, il substrn'est pas inclus dans l'expr de FreeBSD, NetBSD ou OS X. Ce n'est pas une solution portable.
ghoti
@ghoti, notez qu'il substrne s'agit pas à l'origine d'une extension GNU. L'implémentation originale de exprest venue de PWB Unix à la fin des années 70 et avait substr(mais pas :).
Stéphane Chazelas
@ StéphaneChazelas, merci d'avoir ajouté une perspective historique. :) Bien que je sois certain que l'utilisation du PWB n'est pas pertinente pour l'OP, il est toujours amusant de suivre les fonctionnalités et les changements au fil des décennies. GNU a tendance à être la valeur par défaut de nombreuses personnes, mais en général, je pense que j'éviterais d'utiliser des options qui ne sont pas clairement POSIX et qui sont connues pour être absentes des principaux unités.
ghoti
5

cut -c

Si la variable ne contient pas de sauts de ligne, vous pouvez faire:

myvar='abc'
printf '%s\n' "$myvar" | cut -c2

les sorties:

b

awk substr est une autre alternative POSIX qui fonctionne même si la variable a des retours à la ligne:

myvar="$(printf 'a\nb\n')" # note that the last newline is stripped by
                           # the command substitution
awk -- 'BEGIN {print substr (ARGV[1], 3, 1)}' "$myvar"

les sorties:

b

printf '%s\n'est d'éviter les problèmes avec les caractères d'échappement: /programming//a/40423558/895245 par exemple:

myvar='\n'
printf '%s\n' "$myvar" | cut -c1

sorties \comme prévu.

Voir aussi: /programming/1405611/extracting-first-two-characters-of-a-string-shell-scripting

Testé dans Ubuntu 19.04.

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
la source
printf '%s' "$myvar" | cut -c2n'est pas POSIX car la sortie de printfn'est pas du texte sauf si$myvar se termine par un caractère de nouvelle ligne. Il suppose par ailleurs que la variable ne contient pas de caractères de nouvelle ligne car elle cutcoupe chaque ligne de son entrée.
Stéphane Chazelas
L' awkun serait plus efficace et fiable avecawk -- 'BEGIN {print substr (ARGV[1], 2, 1)}' "$myvar"
Stéphane Chazelas
Notez qu'avec les versions actuelles de GNU cut, cela ne fonctionne pas pour les caractères multi-octets (idem pour mawk ou busybox awk)
Stéphane Chazelas
@ StéphaneChazelas merci pour les points. Je n'ai pas compris ce que vous voulez dire dans le premier: voulez-vous dire que printf 'abc '| cut -c2c'est faux parce que non \n(ce que je ne sais pas), ou que la commande échouera si myvar a des nouvelles lignes (ce que je suis d'accord)?
Ciro Santilli 6 改造 中心 法轮功 六四 事件
1
Le comportement de cutn'est pas spécifié si l'entrée n'est pas du texte (bien que des cutimplémentations soient nécessaires pour gérer les lignes ou la longueur arbitraire). La sortie de printf abcn'est pas du texte car elle ne se termine pas par un caractère de nouvelle ligne. En pratique, selon l'implémentation, si vous dirigez cela vers cut -c2, vous obtenez soit b, b<newline>soit rien du tout. Vous auriez besoinprintf 'abc\n' | cut -c2 d'obtenir un comportement spécifié par POSIX (qui est requis pour sortir b<newline>)
Stéphane Chazelas
1

Avec zshou yash, vous utiliseriez:

$ text='€$*₭£'
$ printf '%s\n' "${text[3]}"
*

(dans zsh, vous pouvez le raccourcir en printf '%s\n' $text[3]).

Stéphane Chazelas
la source
0

Vous pouvez utiliser la commande cut. Pour obtenir la 3e position:

echo "SAMPLETEXT" | cut -c3

Vérifiez ce lien http://www.folkstalk.com/2012/02/cut-command-in-unix-linux-examples.html

( Cas avancés ) Cependant, la modification d'IFS est également une bonne chose, surtout lorsque votre entrée peut avoir des espaces. Dans ce cas uniquement, utilisez celui ci-dessous

saveifs=$IFS
IFS=$(echo -en "\n\b")
echo "SAMPLETEXT" | cut -c3
IFS=$saveifs
Midhun Jose
la source
Je ne vois pas comment IFSentrerait en jeu dans le code que vous avez publié.
Kusalananda