Que signifie cet étrange symbole “:>” en bash

47

J'ai trouvé quelque chose dans un script, mais n'appartenant pas au script principal. Il y avait :>une ligne.

Pourriez-vous m'expliquer ce que cela signifie?

:> file
while read A B C D E; do echo "$A;$B;$D;$E;$C" >> file; done < otherfile
diego9403
la source
6
Fait important, :>n'est pas un seul opérateur. Il sera peut-être plus facile de comprendre si vous le lisez tel quel : > file.
jpfx1342
Cela signifie que la personne qui écrit le script aurait dû rediriger la sortie de la boucle du fichier: while read A B C D E; do echo "$A;$B;$D;$E;$C"; done < otherfile > file. Ou mieux encore, ils auraient dû utiliser le bon outil pour le travail, awk, comme suggéré par Peter . En passant-rread , vous voulez presque toujours utiliser le commutateur avec .
Tom Fenech le
En dehors de Bash, ce serait un smiley pour un corbeau.
smci

Réponses:

46

Il y avait:> dans une ligne d'un script bash. Qu'est-ce que ça veut dire?

:> file

C'est un moyen court de dire:

  • Si filen'existe pas, créez-le, sinon tronquez-le en 0octets.

Cela signifie que vous pouvez être sûr qu'il fileexiste et qu'il est vide.

Vous pouvez également utiliser > filemais :> fileest plus portable.

Voir la question relative au débordement de pile. Quel est le but de la commande intégrée:: (deux points) GNU Bash? pour plus d'informations.

DavidPostill
la source
Je ne comprends pas la deuxième ligne. Je pensais que lire les variables de lecture. L'écho de commande est étrange aussi. Pourriez-vous expliquer?
diego9403
Je ne suis pas un expert Unix, mais je pense que la deuxième ligne lit les choses otherfileet echoles diffuse file. Il crée également des variables à partir de ce qu'il lit. Si vous souhaitez une réponse définitive, posez votre propre question.
DavidPostill
2
@ diego9403: readreçoit les entrées de stdin. Seul, il lirait ce que vous tapez. Puisque stdin a été redirigé vers, <otherfilele contenu de otherfile"est saisi" dans stdin. Donc, readobtient les valeurs ligne par ligne dans les variables $ A, $ B, $ C, $ D et $ E.
slebetman
Donc, c'est juste une alternative plus obscure à truncatepartir de coreutils?
Federico Poloni
1
@PeterCordes Je ne voulais pas dire "obscur" comme dans "c'est inhabituel", mais comme dans "c'est moins clair pour le lecteur".
Federico Poloni
29

Cela ressemble à une manière élégante de créer un nouveau fichier. In bash :est une commande nulle:

$ type : 
: is a shell builtin 
$ help : 
:: :
    Null command.

    No effect; the command does nothing.

    Exit Status:
    Always succeeds.

>redirige la sortie de :vers un fichier.

Arkadiusz Drabczyk
la source
2
Il tronquera également le fichier s'il existe déjà ...
DavidPostill
2
oui, c'est ce qui >fait
Arkadiusz Drabczyk
2
:est un raccourci pour true. Peut-être dans certains coquillages, trueun incorporé? Les deux sont intégrés à bash.
Peter Cordes
12

:est un autre nom pour true. Les deux sont des commandes shell dans bash, mais non /bin/:, seulement un /bin/true. La redirection de sortie entraîne le shell dans open(2)le fichier avec O_CREAT|O_TRUNC. Si rien n'est écrit, il reste à la longueur zéro.

Assembler ces deux éléments :> fileest un idiome assez courant pour tronquer des fichiers. La plupart des gens essaient cependant de rendre cela moins étrange en écrivant : >file.


Comme vous avez demandé dans un commentaire sur la deuxième ligne, je vais transformer mes commentaires en réponse. (même si vous ne l'avez pas demandé dans votre question.)

La deuxième ligne est une boucle qui lit les lignes otherfiledans certaines variables nommées. Le corps de la boucle les utilise echopour les imprimer avec des ;séparateurs au lieu des espaces qu’ils avaient auparavant. fileest fermé et rouvert (pour ajouter) chaque itération, car la redirection est à l'intérieur de la boucle. L'utilisation while ...;do read -r ...;done <otherfile >fileserait moins efficace, et éviterait de tronquer le fichier en premier. read -rne mange pas \comme un personnage d'évasion.

Le traitement du texte à bash est assez lent. Une partie de cela est inévitable: readdoit aller un octet à la fois (un read(2)appel système par octet) pour éviter de dépasser la fin d'une ligne. Il serait préférable d'utiliser le bon outil pour le travail:

awk -vOFS=';' '{ print $1, $2, $4, $5, $3 }' -- otherfile  >file

--signifie que votre script ne casse pas si otherfileon lui donne un nom idiot --version.

Définir le séparateur de champ de sortie sur ;signifie que vous pouvez simplement transmettre plusieurs champs en tant qu'arguments à imprimer. Shell readattribue le reste de la ligne avec les espaces à la dernière variable, mais il n’ya aucun moyen de dire à awk de se scinder uniquement en 5. Si cela est important, continuez simplement à utiliser une boucle bash, car c’est gênant dans awk. Perl facilite cela, car il splitpeut prendre beaucoup d'arguments, mais il est beaucoup plus lent à démarrer que awk.

En fait, ce n’était pas si difficile, c’était juste une regex laide à écrire. Pour obtenir le reste de la ligne au lieu d' $5awk, le bouclage sur les champs perd leur espace d'origine. Ma première idée viable est d'utiliser gensubsur $0(toute la ligne) pour supprimer les 4 premiers champs (c'est-à-dire les non-espaces suivis par des espaces), en laissant tout le reste:

awk -vOFS=';' '{ tail = gensub("[[:space:]]*([^[:space:]]+[[:space:]]+){4}", "", 1); print $1, $2, $4, tail, $3 }' -- otherfile >file

J'ai eu raison dès le premier essai, mais le fait d'être impressionné par moi en dit long sur la lisibilité de ce code awk. >. <

Notez comme c'est le même printqu'auparavant, mais tailà la place de $5.

echo 'A  B c DD    e      f g    f' | 
  awk -vOFS=\; '{ tail = gensub("[[:space:]]*([^[:space:]]+[[:space:]]+){4}", "", 1);
   print $1, $2, $4, tail, $3 }'

A;B;DD;e       f g    f;c

Ce serait plus impressionnant si je pouvais copier / coller le littéral et montrer que cela venait dans la sortie. Tapez un en bash avec ^ Q. ctrl-Q signifie Citer la touche suivante en tant que caractère littéral, car l'édition des lignes de style emacs de bash est identique à celle d'emacs.

http://mywiki.wooledge.org/BashFAQ contient des informations utiles sur les scripts d'une manière qui ne casse pas les données ou les noms de fichiers que vous envoyez au script.

Peter Cordes
la source