Quelle est la signification exacte de IFS = $ '\ n'?

125

Si l'exemple suivant, qui définit la IFSvariable d'environnement sur un caractère de saut de ligne ...

IFS=$'\n'
  • Que signifie exactement le signe dollar ?
  • Que fait-il dans ce cas précis?
  • Où puis-je en savoir plus sur cette utilisation spécifique (Google n'autorise pas les caractères spéciaux dans les recherches et je ne sais pas quoi chercher autrement)?

Je sais ce qu'est la IFSvariable d'environnement et quel est le \ncaractère (saut de ligne), mais pourquoi ne pas simplement utiliser le formulaire suivant: IFS="\n"(qui ne fonctionne pas)?

Par exemple, si je veux parcourir chaque ligne d'un fichier et que je veux utiliser une boucle for, je pourrais le faire:

for line in (< /path/to/file); do
    echo "Line: $line"
done

Cependant, cela ne fonctionnera pas correctement à moins qu'il ne IFSsoit défini sur un caractère de saut de ligne. Pour que cela fonctionne, je devrais faire ceci:

OLDIFS=$IFS
IFS=$'\n'
for line in (< /path/to/file); do
    echo "Line: $line"
done
IFS=$OLDIFS

Note: je n'ai pas besoin d'un autre moyen pour faire la même chose, j'en connais déjà beaucoup d'autres ... Je ne suis curieux que de ça $'\n'et je me demande si quelqu'un pourrait me donner une explication à ce sujet.

Yanick Girouard
la source

Réponses:

161

N'interprète normalement bashpas les séquences d'échappement dans les chaînes littérales. Donc, si vous écrivez \nou "\n"ou '\n', ce n'est pas un saut de ligne - c'est la lettre n(dans le premier cas) ou une barre oblique inverse suivie de la lettre n(dans les deux autres cas).

$'somestring'est une syntaxe pour les littéraux de chaîne avec des séquences d'échappement . Donc, contrairement à '\n', $'\n'est en fait un saut de ligne.

sepp2k
la source
2
Pas exactement - \nest juste une lettre n (échappée). Vous avez raison '\n'et vous "\n"êtes contrecarré suivi de n.
Roman Cheplyaka
15
Notez que cela $'\n'est spécifique à bash - cela ne fonctionnera pas dans un shell POSIX ( /bin/sh). Pour obtenir le même effet d'une manière compatible POSIX, vous pouvez taper IFS=', puis appuyer sur retour pour taper un caractère de nouvelle ligne réel, puis taper la fermeture'
Richard Hansen
23
IFS=$(echo -e '\n')devrait également le faire d'une manière compatible POSIX.
Vineet
12
@Vineet - cela m'a donné une pause pour contester un commentaire positif. Bien que ce soit Posix-correct, cela ne fonctionne pas - Les opérateurs de substitution de commande dans bash suppriment tous les caractères de fin de ligne. Voir ceci pour plus de détails .
Digital Trauma
9
@DigitalTrauma Je pense que ce n'est même pas POSIX: -en'est pas défini, et \nsans -efonctionne comme une extension XSI: pubs.opengroup.org/onlinepubs/9699919799/utilities/… . printf '\n'roches;)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
21

Juste pour donner à la construction son nom officiel : les chaînes du formulaire $'...'sont appelées chaînes entre guillemets C ANSI .

Autrement dit, comme dans les chaînes [ANSI] C, les séquences d'échappement de backlash sont reconnues et étendues à leur équivalent littéral (voir ci-dessous pour la liste complète des séquences d'échappement prises en charge).

Après cette expansion, les $'...'chaînes se comportent de la même manière que les '...'chaînes - c'est-à-dire qu'elles sont traitées comme des littéraux NON soumis à d'autres extensions de shell .

Par exemple, se $'\n'développe en un caractère de nouvelle ligne littéral - ce qui est quelque chose qu'un littéral de chaîne bash ordinaire (que ce soit '...'ou "...") ne peut pas faire. [1]

Une autre caractéristique intéressante est que les chaînes ANSI entre guillemets C peuvent s'échapper '(guillemets simples) comme\' , ce qui, '...'(chaînes régulières entre guillemets simples) ne peut pas:

echo $'Honey, I\'m home' # OK; this cannot be done with '...'

Liste des séquences d'échappement prises en charge :

Les séquences d'échappement de backslash, si présentes, sont décodées comme suit:

\ une alerte (cloche)

\ b retour arrière

\ e \ E un caractère d'échappement (pas ANSI C)

\ f form feed

\ n nouvelle ligne

\ r retour chariot

\ t onglet horizontal

\ v onglet vertical

\ barre oblique inverse

\' simple citation

\" double citation

\ nnn le caractère de huit bits dont la valeur est la valeur octale nnn (un à trois chiffres)

\ xHH le caractère de huit bits dont la valeur est la valeur hexadécimale HH (un ou deux chiffres hexadécimaux)

\ uHHHH le caractère Unicode (ISO / CEI 10646) dont la valeur est la valeur hexadécimale HHHH (un à quatre chiffres hexadécimaux)

\ UHHHHHHHH le caractère Unicode (ISO / CEI 10646) dont la valeur est la valeur hexadécimale HHHHHHHH (un à huit chiffres hexadécimaux)

\ cx un caractère control-x

Le résultat développé est entre guillemets simples, comme si le signe dollar n’était pas présent.


[1] Vous pouvez, cependant, incorporer des nouvelles lignes réelles dans les chaînes '...' et "..."; c'est-à-dire que vous pouvez définir des chaînes qui s'étendent sur plusieurs lignes.

mklement0
la source
16

De http://www.linuxtopia.org/online_books/bash_guide_for_beginners/sect_03_03.html :

Les mots de la forme "$ 'STRING'" sont traités d'une manière spéciale. Le mot se développe en une chaîne, avec des caractères d'échappement avec barre oblique inverse remplacés comme spécifié par la norme ANSI-C. Les séquences d'échappement de backslash peuvent être trouvées dans la documentation de Bash.

Je suppose que cela force le script à échapper au saut de ligne selon la norme ANSI-C appropriée.

Brad Swerdfeger
la source
8

Récupération de l'IFS par défaut - ce OLDIFS=$IFSn'est pas nécessaire. Exécutez un nouvel IFS dans le sous-shell pour éviter de remplacer l'IFS par défaut:

ar=(123 321); ( IFS=$'\n'; echo ${ar[*]} )

De plus, je ne crois pas vraiment que vous récupériez complètement l'ancien IFS. Vous devez le double guillemet pour éviter les sauts de ligne tels que OLDIFS="$IFS".

Marek
la source
2
c'est une technique vraiment utile. Je viens d' utiliser pour une coquille propre op rejoindre: args=$(IFS='&'; echo "$*"). restaurer IFSd' $' \t\n'une manière conviviale Bourne shell n'est pas une mince affaire.
jeberle
Re Besides I don't really believe you recover the old IFS fully: le fractionnement de mots n'est pas effectué sur le RHS des affectations de variables (mais la suppression des guillemets l'est), donc OLDIFS=$IFSet OLDIFS="$IFS"se comporte de la même manière.
mklement0
3

Les chaînes entre guillemets C ANSI sont un point clé. Merci à @ mklement0.

Vous pouvez tester les chaînes entre guillemets C ANSI avec la commande od.

echo -n $'\n' | od -c
echo -n '\n' | od -c
echo -n $"\n" | od -c
echo -n "\n" | od -c

Les sorties:

0000000  \n  
0000001

0000000   \   n   
0000002

0000000   \   n   
0000002

0000000   \   n   
0000002

Vous pouvez connaître la signification clairement par les sorties.

Grand bouclier
la source
-7

C'est comme récupérer la valeur d'une variable:

VAR='test'
echo VAR
echo $VAR

sont différents, de sorte que le signe dollar évalue essentiellement le contenu.

Pieter
la source
6
Cela n'a rien à voir avec les variables. $'FOO'(contrairement à $FOOce dont il ne s'agissait pas) est une chaîne littérale. Si vous exécutez echo $'VAR', vous verrez qu'il imprime la chaîne VAR, non test.
sepp2k