75 votes

Comment écrire un script qui accepte les entrées d'un fichier ou de stdin ?

Comment peut-on écrire un script qui accepte des entrées provenant soit d'un argument de type nom de fichier, soit de stdin ?

par exemple, vous pouvez utiliser less De cette façon, on peut exécuter less filename et de manière équivalente cat filename | less .

Existe-t-il un moyen simple de le faire ? ou dois-je réinventer la roue et écrire un peu de logique dans le script ?

75voto

suspectus Points 4495

Si l'argument fichier est le premier argument de votre script, vérifiez qu'il existe un argument ( $1 ) et qu'il s'agit d'un fichier. Sinon, lire l'entrée depuis stdin -

Ainsi, votre script pourrait contenir quelque chose comme ceci :

#!/bin/bash
[ $# -ge 1 -a -f "$1" ] && input="$1" || input="-"
cat $input

Par exemple, vous pouvez appeler le script comme suit

./myscript.sh filename

ou

who | ./myscript.sh

Modifier Quelques explications sur le script :

[ $# -ge 1 -a -f "$1" ] - Si au moins un argument de ligne de commande ( $# -ge 1 ) ET (opérateur -a) le premier argument est un fichier (-f teste si "$1" est un fichier) alors le résultat du test est vrai.

&& est l'opérateur logique AND de Shell. Si le test est vrai, alors on assigne input="$1" et cat $input produira le fichier.

|| est l'opérateur logique OR Shell. Si le test est faux, alors les commandes suivantes || sont analysés. L'entrée est assignée à "-". La commande cat - lit sur le clavier.

En résumé, si l'argument script est fourni et qu'il s'agit d'un fichier, alors la variable d'entrée est affectée au nom du fichier. S'il n'y a pas d'argument valide, alors cat lit à partir du clavier.

15voto

Yagel Points 131

read lit depuis l'entrée standard. En le redirigeant depuis le fichier ( ./script <someinput ) ou par un tuyau ( dosomething | ./script ) ne le fera pas fonctionner différemment.

Tout ce que vous avez à faire est de boucler sur toutes les lignes de l'entrée (et cela ne diffère pas de l'itération sur les lignes du fichier).

(exemple de code, ne traite qu'une seule ligne)

#!/bin/bash

read var
echo $var

Écho de la première ligne de votre entrée standard (soit par l'intermédiaire de < ou | ).

5voto

Jacob Hayes Points 51

Vous ne mentionnez pas quel Shell vous prévoyez d'utiliser, donc je vais supposer bash, bien que ces choses soient assez standard dans tous les shells.

Arguments pour les fichiers

Les arguments sont accessibles via les variables $1 - $n ( $0 renvoie la commande utilisée pour exécuter le programme). Disons que j'ai un script qui ne fait que cat sort un nombre n de fichiers avec un délimiteur entre eux :

#!/usr/bin/env bash
#
# Parameters:
#    1:   string delimiter between arguments 2-n
#    2-n: file(s) to cat out
for arg in ${@:2} # $@ is the array of arguments, ${@:2} slices it starting at 2.
do
   cat $arg
   echo $1
done

Dans ce cas, nous passons un nom de fichier à cat. Toutefois, si vous souhaitez transformer les données du fichier (sans les écrire et les réécrire explicitement), vous pouvez également stocker le contenu du fichier dans une variable :

file_contents=$(cat $filename)
[...do some stuff...]
echo $file_contents >> $new_filename

Lire à partir de stdin

En ce qui concerne la lecture de stdin, la plupart des shells ont une fonction assez standard read bien qu'il y ait des différences dans la façon dont les invites sont spécifiées (au minimum).

El Page de manuel de Bash builtins a une explication assez concise de read mais je préfère le Hackers du Bash page.

Tout simplement :

read var_name

Variables multiples

Pour définir plusieurs variables, il suffit de fournir plusieurs noms de paramètres à la commande read :

read var1 var2 var3

read placera alors un mot de stdin dans chaque variable, en vidant tous les mots restants dans la dernière variable.

 read var1 var2 var3
thing1 thing2 thing3 thing4 thing5
 echo $var1; echo $var2; echo $var3
thing1
thing2
thing3 thing4 thing5

Si le nombre de mots saisis est inférieur au nombre de variables, les variables restantes seront vides (même si elles ont été définies précédemment) :

 read var1 var2 var3
thing1 thing2
 echo $var1; echo $var2; echo $var3
thing1
thing2
# Empty line

Prompts

J'utilise -p souvent le drapeau pour une invite :

read -p "Enter filename: " filename

Remarque : ZSH et KSH (et peut-être d'autres) utilisent une syntaxe différente pour les invites :

read "filename?Enter filename: " # Everything following the '?' is the prompt

Valeurs par défaut

Ce n'est pas vraiment un read mais je l'utilise beaucoup en conjonction avec read . Par exemple :

read -p "Y/[N]: " reply
reply=${reply:-N}

En gros, si la variable (reply) existe, elle est renvoyée, mais si elle est vide, elle renvoie le paramètre suivant ("N").

4voto

Le moyen le plus simple est de rediriger vous-même stdin :

if [ "$1" ] ; then exec < "$1" ; fi

Ou si vous préférez la forme la plus laconique :

test "$1" && exec < "$1"

Maintenant, le reste de votre script peut simplement lire depuis stdin. Bien sûr, vous pouvez faire la même chose avec une analyse plus avancée des options plutôt que de coder en dur la position du nom de fichier comme suit "$1" .

4voto

Sammy Points 41

Vous pouvez aussi le faire :

#!/usr/bin/env bash

# Set variable input_file to either $1 or /dev/stdin, in case $1 is empty
# Note that this assumes that you are expecting the file name to operate on on $1
input_file="${1:-/dev/stdin}"

# You can now use "$input_file" as your file to operate on
cat "$input_file"

Pour d'autres astuces de substitution de paramètres en Bash, voir ce .

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X