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


57

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

par exemple, vous pouvez utiliser lesscette méthode. on peut exécuter less filenameet de manière équivalente cat filename | less.

y a-t-il un moyen facile de le faire "hors de la boîte"? ou dois-je réinventer la roue et écrire un peu de logique dans le script?


@PlasmaPower Tant que la question concerne SU, il n'est pas nécessaire de poser la question sur un autre site SE. Beaucoup de sites SE ont des chevauchements; En règle générale, il n'est pas nécessaire de suggérer un site qui se chevauche, sauf si la question est hors sujet (dans ce cas, votez pour la migration) ou sur le sujet mais ne suscite pas beaucoup de réponses (dans ce cas, le demandeur doit signaler au modérateur). attention / migration, pas cross-post).
Bob

Réponses:


59

Si l'argument de 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 les entrées de stdin -

Donc, 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

./myscript.sh filename

ou

who | ./myscript.sh

Edit Quelques explications du script:

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

&&est l'opérateur logique AND du shell. Si test est vrai, attribuez input="$1"et cat $inputle fichier sera généré.

||est l'opérateur OU logique du shell. Si le test est faux, les commandes suivantes ||sont analysées. l'entrée est assignée à "-". La commande cat -lit depuis le clavier.

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


que fait && input="$1" || input="-" -il et pourquoi est-il en dehors de l' testopérateur?
cmo

J'ai ajouté une édition avec quelques explications qui, je l'espère, aideront.
suspectus

Que se passe-t-il si le script a plusieurs arguments ( $@)?
g33kz0r

12

readlit à partir de l'entrée standard. Le rediriger depuis file ( ./script <someinput) ou via pipe ( dosomething | ./script) ne le fera pas fonctionner différemment.

Tout ce que vous avez à faire est de parcourir toutes les lignes en entrée (et cela ne diffère pas d’itérer sur les lignes du fichier).

(exemple de code, traite une seule ligne)

#!/bin/bash

read var
echo $var

Répondra à la première ligne de votre entrée standard (via <ou |).


Merci! J'ai choisi l'autre réponse parce que cela me convenait mieux. J'emballais un autre script et je ne voulais pas faire de boucle tant que toutes les entrées n'étaient pas reçues (cela pourrait être beaucoup d'entrées ... ce serait un gaspillage).
gilad hoch

4

Vous ne mentionnez pas le shell que vous comptez utiliser, alors je suppose que bash, bien que ce soient des choses assez standard pour tous les obus.

Arguments de fichier

Les arguments sont accessibles via les variables $1- $n( $0retourne la commande utilisée pour exécuter le programme). Supposons que j’ai un script qui catcontient un nombre n de fichiers avec un séparateur 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 ni 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 de stdin

En ce qui concerne stdin, la plupart des interpréteurs ont un standard assez readintégré, bien qu'il existe des différences dans la manière dont les invites sont spécifiées (au moins).

La page de manuel de Bash builtins contient une explication assez concise read, mais je préfère la page Bash Hackers .

Simplement:

read var_name

Plusieurs variables

Pour définir plusieurs variables, indiquez simplement plusieurs noms de paramètres read:

read var1 var2 var3

read placera ensuite un mot de stdin dans chaque variable, 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 moins de mots sont entrés que de variables, les variables restantes seront vides (même si elles avaient été définies précédemment):

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

Instructions

J'utilise -psouvent flag 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

Les valeurs par défaut

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

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

En gros, si la variable (réponse) existe, retourne elle-même, mais si est vide, retourne le paramètre suivant ("N").


4

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

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

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

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

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


execessaierons d'exécuter l'argument comme une commande qui n'est pas ce que nous voulons ici.
Suzana

@Suzana_K: Pas quand il n'y a pas d'argument, comme ici. Dans ce cas, il ne fait que remplacer les descripteurs de fichier pour le shell plutôt que pour un processus enfant.
R ..

J'ai copié if [ "$1" ] ; then exec < "$1" ; fidans un script de test et cela donne un message d'erreur parce que la commande est inconnue. Même avec la forme laconique.
Suzana

1
@Suzana_K: Quel shell utilisez-vous? Si cela est vrai, il ne s'agit pas d'une implémentation fonctionnelle de la commande POSIX sh / Bourne shell.
R ..

GNU bash 4.3.11 sur Linux Mint Qiana
Suzana

3

utiliser (ou enchaîner) quelque chose d'autre qui se comporte déjà de cette façon, et utiliser "$@"

disons que je veux écrire un outil qui remplacera des séries d'espaces dans le texte avec des onglets

trest la façon la plus évidente de le faire, mais il n'accepte stdin, nous avons donc à la chaîne hors de :cat

$ cat entab1.sh
#!/bin/sh

cat "$@"|tr -s ' ' '\t'
$ cat entab1.sh|./entab1.sh
#!/bin/sh

cat     "$@"|tr -s      '       '       '\t'
$ ./entab1.sh entab1.sh
#!/bin/sh

cat     "$@"|tr -s      '       '       '\t'
$ 

pour un exemple où l'outil utilisé se comporte déjà de cette façon, nous pourrions le réimplémenter avec à la sedplace:

$ cat entab2.sh
#!/bin/sh

sed -r 's/ +/\t/g' "$@"
$ 

3

Vous pouvez aussi 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 plus d’astuces de substitution de paramètres dans Bash, voyez ceci .


1
C'est fantastique! J'utilise uglifyjs < /dev/stdinet ça marche à merveille!
Fregante

0

Vous pouvez également rester simple et utiliser ce code


Lorsque vous créez un fichier de script pass_it_on.sh avec ce code,

#!/bin/bash

cat

Tu peux courir

cat SOMEFILE.txt | ./pass_it_on.sh

et tout le contenu de stdin sera simplement diffusé à l'écran.


Vous pouvez également utiliser ce code pour conserver une copie de stdin dans un fichier, puis la cracher à l'écran.

#!/bin/bash

tmpFile=`mktemp`
cat > $tmpFile
cat $tmpFile    

et voici un autre exemple, peut-être plus lisible, expliqué ici:

http://mockingeye.com/blog/2013/01/22/reading-everything-stdin-in-a-bash-script/

#!/bin/bash

VALUE=$(cat)

echo "$VALUE"

S'amuser.

RaamEE


0

Le moyen le plus simple et compatible POSIX est:

file=${1--}

ce qui équivaut à ${1:--}.

Ensuite, lisez le fichier comme d'habitude:

while IFS= read -r line; do
  printf '%s\n' "$line" # Or: env POSIXLY_CORRECT=1 echo "$line"
done < <(cat -- "$file")
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.